15

Tengo un árbol de objetos que estoy serializando en JSON con DataContractJsonSerializer. Dictionary<TKey, TValue> consigue serializado, pero no me gusta el margen de beneficio - los artículos no se representan así:Serialize Dictionary <TKey, TValue> a JSON con DataContractJsonSerializer

{key1:value, key2:value2} 

sino más bien como un conjunto de objetos serializados KeyValuePair<TKey, TValue>:

[{ 
    "__type":"KeyValuePairOfstringanyType:#System.Collections.Generic", 
    "key":"key1", 
    "value":"value1" 
},  
{ 
    "__type":"KeyValuePairOfstringanyType:#System.Collections.Generic", 
    "key":"key2", 
    "value":"value2" 
}] 

feo, no es lo ?

Así que evito esto envolviendo el Diccionario genérico en un objeto personalizado que implementa ISerializable, e implemento mi serialización personalizada en el método GetObjectData (y solo toma 3 líneas).

Ahora el problema - no puedo hacer que mi clase se derivan de Dictionary<TKey, TValue>, así que implementar toda la lógica (Add, Clear, etc.) en mi clase personalizada, que se aplica a un campo privado Dictionary<TKey, TValue>. La herencia sería preferible, ya que tendré toda la funcionalidad del Diccionario genérico a mi disposición cuando use mi objeto personalizado.

El problema con la herencia es que Dictionary<TKey, TValue> implementa ISerializable por su propia cuenta, y DataContractJsonSerializer parece preferir esta aplicación incluso si implemento ISerializable explícitamente de mi clase personalizada, así:

public class MyClass : Dictionary<string, object>, ISerializable 
{ 
    public override void GetObjectData(SerializationInfo info, 
     StreamingContext context) 
} 

Estaba realmente sorprendido de que esto es posible ya que me permite implementar la misma interfaz dos veces sin ser obviamente capaz de usar la implementación de interfaz explícita, así que analicé la situación con más detalle en una publicación de blog about multiple interface implementation

Así que, de acuerdo con los experimentos que hice allí, el serializador debe ser llamar a mi aplicación de ISerializable, no importa qué tipo de fundición se utiliza internamente -

((ISerializable)((Dictionary<,>)obj)).GetObjectData(...) 

o:

((ISerializable)obj).GetObjectData(...) 

pero al parecer no es sucediendo como veo en el JSON resultante que aún se llama al serializador KeyValuePair<TKey, TValue>. ¿Qué podría estar pasando que me estoy perdiendo?

Actualización: Las respuestas y los comentarios que recibo hasta ahora son solo sugerencias de soluciones.Me ha señalado, sin embargo, que tengo una solución que funciona bastante bien por lo que con esta pregunta tengo 2 objetivos:

  1. Eventualmente hacer que funcione con el diseño original - y yo no voy a cambiar la lógica de serialización solo por eso, hay un montón de código y lógica dependiente

  2. Para entender el misterio de por qué no está el DataContractJsonSerializer usando mi código de serialización, como se puede ver en la publicación del blog que mencioné, hice todo tipo de experimentos con la implementación de la interfaz y la herencia, y estaba seguro de que comprendía todos los pormenores del proceso, así que me preocupa no entender lo que está pasando en este proceso. caso

+1

Parece que podría envolver el diccionario con una clase IEnumerable y su implementación ISerializable. Mucho menos trabajo que tratar de heredar de un diccionario. –

+0

También podría usar el 'JavaScriptSerializer' ya que serializa los diccionarios como prefiera. –

+0

@RitchMelton: heredar del diccionario no es mucho trabajo, pero debo admitir que implementar IEnumerable y devolver el getEnumarator del diccionario miembro a primera vista parece ser mejor que mi solución alternativa: solo un método para implementar – Vroomfundel

Respuesta

1

Parece, there is no way de personalizar DataContractJsonSerializer.

Si aún desea lograr lo que desea, considere usar Json.Net. Es más rápido y más flexible que DataContractJsonSerializer. Mire la concepción de JsonConverter de Json.Net. Le da la posibilidad de personalizar el proceso de serialización/deserialización.

Además, la implementación predeterminada de la serialización de diccionarios es exactamente la que desea http://james.newtonking.com/projects/json/help/SerializingCollections.html.

+1

Gracias por la respuesta. El artículo vinculado de Dan Rigsby solo menciona que DataContractSerializer no es configurable, pero no va tan lejos como las longitudes en las que he ido explorando por qué no se llama a la implementación explícita de GetObjectData. Aparte de eso, conozco bien a json.net, y sé que es bueno, solo cambiar el motor de serialización en el proyecto tendrá un impacto demasiado grande. No es solo mi diccionario personalizado el que está siendo serializado – Vroomfundel

+0

El enlace de Rigsby ahora está muerto (DNS fracaso). Todos los mecanismos de personalización de serialización estándar están disponibles, porque todo esto se basa en 'XmlObjectSerializer'. Desafortunadamente, esto no es "simple" para aprovechar (ver la respuesta de "propiedad sustituta"), pero está lejos de ser "de ninguna manera". –

0

Como se mencionó @Pashec, si utiliza Json.NET, puede intentar lo siguiente:

public Message <methodname> { 
    ... 
    string jsonMessage = JsonConvert.SerializeObject(myObjectTree); 
    return WebOperationContext.Current.CreateTextResponse(jsonMessage, "application/javascript; charset=utf-8", Encoding.UTF8); 
} 
4

Una opción es usar una propiedad de alquiler y tiene el diccionario sea dentro de la medida Tipo ISerializable, de esa manera se no necesita preocuparse por la herencia:

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

[DataMember(Name="NodeData")] 
private CustomDictionarySerializer NodeDataSurrogate 
{ 
    get 
    { 
     return new CustomDictionarySerializer(NodeData); 
    } 
    set 
    { 
     NodeData = value._data; 
    } 
} 

[Serializable] 
private class CustomDictionarySerializer : ISerializable 
{ 
    public Dictionary<string, string> _data; 

    public CustomDictionarySerializer(Dictionary<string, string> dict) 
    { 
     _data = dict; 
    } 

    public CustomDictionarySerializer(SerializationInfo info, StreamingContext context) 
    { 
     _data = new Dictionary<string, string>(); 
     var valueEnum = info.GetEnumerator(); 
     while(valueEnum.MoveNext()) 
     { 
      _data[valueEnum.Current.Name] = valueEnum.Current.Value.ToString(); 
     } 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     foreach (var pair in _data) 
     { 
      info.AddValue(pair.Key, pair.Value); 
     } 
    } 
} 
+0

Si controla las clases, esto funciona muy bien, y puede hacer que el valor 'CustomDictionarySerializer' escriba un parámetro genérico para que pueda serializar (y deserializar) cualquier tipo de diccionario. Tenga en cuenta que cualquier valor de tipo de referencia también agrega una propiedad "__type" (para propósitos de deserialización). También debe usar el atributo 'DataContract' en la clase. –

Cuestiones relacionadas