2011-06-20 14 views
7

Aparentemente, IDictionary<string,object> se serializa como una matriz de objetos KeyValuePair (por ejemplo, [{Key:"foo", Value:"bar"}, ...]). ¿Es posible serializarlo como un objeto en su lugar (por ejemplo, {foo:"bar"})?Serializar diccionarios con JavaScriptSerializer

+3

sí, no haga uso JavaScriptSerializer, su basura por completo. Use Newtonsoft Json.NET –

Respuesta

10

Aunque estoy de acuerdo que JavaScriptSerializer es una porquería y Json.Net es una mejor opción , hay una manera en la que puede hacer que JavaScriptSerializer serialice de la manera que desee. Usted tendrá que registrar un convertidor y reemplazar el método Serialize usar algo como esto:

public class KeyValuePairJsonConverter : JavaScriptConverter 
{ 
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) 
    { 
     var instance = Activator.CreateInstance(type); 

     foreach (var p in instance.GetType().GetPublicProperties()) 
     { 
      instance.GetType().GetProperty(p.Name).SetValue(instance, dictionary[p.Name], null); 
      dictionary.Remove(p.Name); 
     } 

     foreach (var item in dictionary) 
      (instance).Add(item.Key, item.Value); 

     return instance; 
    } 
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) 
    { 
     var result = new Dictionary<string, object>(); 
     var dictionary = obj as IDictionary<string, object>; 
     foreach (var item in dictionary) 
      result.Add(item.Key, item.Value); 
     return result; 
    } 
    public override IEnumerable<Type> SupportedTypes 
    { 
     get 
     { 
      return new ReadOnlyCollection<Type>(new Type[] { typeof(your_type) }); 
     } 
    } 
} 

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); 
javaScriptSerializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJsonConverter() }); 
jsonOfTest = javaScriptSerializer.Serialize(test); 
// {"x":"xvalue","y":"\/Date(1314108923000)\/"} 

Espero que esto ayude!

+0

Estoy tratando de usar su clase, pero parece que su variable 'instance' de' Class Object' no tiene ningún método de agregar. –

4

No, no es posible con JavaScriptSerializer. Es posible con Json.NET:

public class Bar 
{ 
    public Bar() 
    { 
     Foos = new Dictionary<string, string> 
     { 
      { "foo", "bar" } 
     }; 
    } 

    public Dictionary<string, string> Foos { get; set; } 
} 

y luego:

var bar = new Bar(); 
string json = JsonConvert.SerializeObject(bar, new KeyValuePairConverter()); 

produciría la deseada:

{"Foos":{"foo":"bar"}} 
+0

Al principio iba por esa ruta. Ver mi pregunta anterior: http://stackoverflow.com/questions/6416017/json-net-deserializing-nested-dictionaries. Tuve un problema al deserializar diccionarios anidados con Json.NET. – Daniel

+2

@Daniel, Json.NET es azúcar en comparación con 'JavaScriptSerializer'. Entonces, si tiene problemas con Json.NET, no sé qué decir sobre la alternativa :-) En lo que respecta a su otra pregunta, ese es un comportamiento perfectamente normal. Todo lo que indicó al serializador es un diccionario de cadena y objeto. Entonces, estás trabajando con diccionarios de tipo débil en tu código, ¿qué esperas a cambio? Todo lo que puedes obtener es, por supuesto, JObjects de tipeo débil. Use un diccionario fuertemente tipado: 'Dictionary ', habrá una diferencia. –

+0

¿A qué se refiere? No lo estoy viendo – Daniel

1

Pude resolverlo usando JavaScriptSerializer, el truco es crear su propio convertidor. El siguiente código es el código de trabajo:

public class KeyValuePairJsonConverter : JavaScriptConverter { 
    public override object Deserialize(IDictionary<string, object> dictionary 
             , Type type 
             , JavaScriptSerializer serializer) { 
     throw new InvalidOperationException("Sorry, I do serializations only."); 
    } 

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { 
     Dictionary<string, object> result = new Dictionary<string, object>(); 
     Dictionary<string, MyClass> dictionaryInput = obj as Dictionary<string, MyClass>; 

     if (dictionaryInput == null) { 
      throw new InvalidOperationException("Object must be of Dictionary<string, MyClass> type."); 
     } 

     foreach (KeyValuePair<string, MyClass> pair in dictionaryInput) 
      result.Add(pair.Key, pair.Value); 

     return result; 
    } 

    public override IEnumerable<Type> SupportedTypes { 
     get { 
      return new ReadOnlyCollection<Type>(new Type[] { typeof(Dictionary<string, MyClass>) }); 
     } 
    } 
} 

Y aquí es cómo lo usa:

JavaScriptSerializer js = new JavaScriptSerializer(); 
js.RegisterConverters(new JavaScriptConverter[] { new KeyValuePairJsonConverter() }); 
Context.Response.Clear(); 
Context.Response.ContentType = "application/json"; 
Context.Response.Write(js.Serialize(myObject)); 
1

He aquí un Creo versión mejorada de la respuesta Tomas. Funciona de maravilla. También podríamos agregar un cheque para el atributo ScriptIgnore pero bueno, noquearlo.

BTW, elegí JavaScriptSerializer porque en mi opinión las soluciones de terceros son la mayoría del tiempo: menos conocidas, largas de instalar, a menudo olvidadas prerrequisitos y con borrosos estados de copia que hacen que sea riesgoso distribuirlas en los negocios.

P-S: No entendía por qué intentábamos deserializar tanto la instancia como la instancia como un diccionario, así que desgasté esa parte.

public class KeyValuePairJsonConverter : JavaScriptConverter 
{ 
    public override object Deserialize(IDictionary<string, object> deserializedJSObjectDictionary, Type targetType, JavaScriptSerializer javaScriptSerializer) 
    { 
     Object targetTypeInstance = Activator.CreateInstance(targetType); 

     FieldInfo[] targetTypeFields = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance); 

     foreach (FieldInfo fieldInfo in targetTypeFields) 
      fieldInfo.SetValue(targetTypeInstance, deserializedJSObjectDictionary[fieldInfo.Name]); 

     return targetTypeInstance; 
    } 

    public override IDictionary<string, object> Serialize(Object objectToSerialize, JavaScriptSerializer javaScriptSerializer) 
    { 
     IDictionary<string, object> serializedObjectDictionary = new Dictionary<string, object>(); 

     FieldInfo[] objectToSerializeTypeFields = objectToSerialize.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); 

     foreach (FieldInfo fieldInfo in objectToSerializeTypeFields) 
      serializedObjectDictionary.Add(fieldInfo.Name, fieldInfo.GetValue(objectToSerialize)); 

     return serializedObjectDictionary; 
    } 

    public override IEnumerable<Type> SupportedTypes 
    { 
     get 
     { 
      return new ReadOnlyCollection<Type>(new Type[] { typeof(YOURCLASSNAME) }); 
     } 
    } 
} 
+0

Respecto a mi nota de PS. Quizás esto fue para permitir especificar un tipo de diccionario genérico en lugar de un tipo, pero honestamente no veo el punto de especificar un tipo de diccionario genérico, ya que todo esto se trata de deserializar los diccionarios . Y es cubridor. – Olograph

2

que era capaz de resolver con JavaScriptSerializer con LINQ Seleccionar:

var dictionary = new Dictionary<int, string>(); 
var jsonOutput = new JavaScriptSerializer().Serialize(dictionary.Select(x => new { Id = x.Key, DisplayText = x.Value })); 
+0

Esto no funciona en casos complejos (es decir, estructuras jerárquicas complejas), pero seguro resuelve el problema en el caso simple, por lo que es una solución a considerar si desea evitar agregar texto estándar a JavaScriptSerializer o una biblioteca de terceros. –

+0

Para mí funcionó incluso en casos complejos, simplemente cambiando a '.Serialize (dictionary)'. Es más simple que JsonConvert, listo para usar. Ojalá pudiera dar +2 a esta respuesta –

Cuestiones relacionadas