¿Cómo puedo serializar / deserializar un diccionario con claves personalizadas usando Json.Net?

Tengo la siguiente clase, que uso como clave en un diccionario:

public class MyClass { private readonly string _property; public MyClass(string property) { _property = property; } public string Property { get { return _property; } } public override bool Equals(object obj) { MyClass other = obj as MyClass; if (other == null) return false; return _property == other._property; } public override int GetHashCode() { return _property.GetHashCode(); } } 

La prueba que estoy ejecutando está aquí:

  [Test] public void SerializeDictionaryWithCustomKeys() { IDictionary expected = new Dictionary(); expected.Add(new MyClass("sth"), 5.2); JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; string output = JsonConvert.SerializeObject(expected, Formatting.Indented, jsonSerializerSettings); var actual = JsonConvert.DeserializeObject<IDictionary>(output, jsonSerializerSettings); CollectionAssert.AreEqual(expected, actual); } 

La prueba falla, porque Json.Net parece estar utilizando el método ToString() en las teclas del diccionario, en lugar de serializarlas correctamente. El json resultante de la prueba anterior es:

 { "$type": "System.Collections.Generic.Dictionary`2[[RiskAnalytics.UnitTests.API.TestMarketContainerSerialisation+MyClass, RiskAnalytics.UnitTests],[System.Object, mscorlib]], mscorlib", "RiskAnalytics.UnitTests.API.TestMarketContainerSerialisation+MyClass": 5.2 } 

lo cual es claramente incorrecto ¿Cómo puedo hacer que funcione?

Esto debería funcionar:

Publicación por entregas:

 JsonConvert.SerializeObject(expected.ToArray(), Formatting.Indented, jsonSerializerSettings); 

Al llamar a expected.ToArray() está serializando una matriz de KeyValuePair lugar de en el diccionario.

Deserialización:

 JsonConvert.DeserializeObject[]>(output, jsonSerializerSettings).ToDictionary(kv => kv.Key, kv => kv.Value); 

Aquí deserializa la matriz y luego recupera el diccionario con la .ToDictionary(...) .

No estoy seguro si la salida cumple con sus expectativas, pero seguramente pasa la afirmación de igualdad.

La respuesta de Grx70 es buena, solo agrega una solución alternativa aquí. Me encontré con este problema en un proyecto de Web API en el que no llamaba a SerializeObject pero permitía que la serialización tuviera lugar automáticamente.

Este JsonConverter personalizado basado en la respuesta de Brian Rogers a una pregunta similar me funcionó:

 public class DeepDictionaryConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (typeof(IDictionary).IsAssignableFrom(objectType) || TypeImplementsGenericInterface(objectType, typeof(IDictionary< ,>))); } private static bool TypeImplementsGenericInterface(Type concreteType, Type interfaceType) { return concreteType.GetInterfaces() .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Type type = value.GetType(); IEnumerable keys = (IEnumerable)type.GetProperty("Keys").GetValue(value, null); IEnumerable values = (IEnumerable)type.GetProperty("Values").GetValue(value, null); IEnumerator valueEnumerator = values.GetEnumerator(); writer.WriteStartArray(); foreach (object key in keys) { valueEnumerator.MoveNext(); writer.WriteStartArray(); serializer.Serialize(writer, key); serializer.Serialize(writer, valueEnumerator.Current); writer.WriteEndArray(); } writer.WriteEndArray(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } } 

En mi caso, estaba serializando una propiedad Dictionary en una clase donde MyCustomType tenía propiedades como Name y Id . Este es el resultado:

 ... "dictionaryProp": [ [ { "name": "MyCustomTypeInstance1.Name", "description": null, "id": null }, 3 ], [ { "name": "MyCustomTypeInstance2.Name", "description": null, "id": null }, 2 ] ] ...