2014-07-10 6 views
5

Ich habe die folgende Klasse, dass ich als ein Schlüssel in einem Wörterbuch verwenden:Wie kann ich ein Wörterbuch mit benutzerdefinierten Schlüsseln mithilfe von Json.Net serialisieren/deserialisieren?

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(); 
     } 
    } 

Der Test bei mir läuft hier:

[Test] 
    public void SerializeDictionaryWithCustomKeys() 
    { 
     IDictionary<MyClass, object> expected = new Dictionary<MyClass, object>(); 
     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<MyClass, object>>(output, jsonSerializerSettings); 
     CollectionAssert.AreEqual(expected, actual); 
    } 

Der Test schlägt fehl, weil Json.Net zu scheint Verwenden Sie die ToString()-Methode für die Wörterbuchschlüssel, anstatt sie ordnungsgemäß zu serialisieren. Der resultierende JSON aus dem obigen Test lautet:

was eindeutig falsch ist. Wie kann ich es zur Arbeit bringen?

+0

JSON nicht immer Wörterbücher Griff sehr gut, warum Sie nicht einfach außer Kraft setzen '.ToString()' 'der MyClass'? –

+0

Warum so ein komplexer Wörterbuchschlüssel? Warum nicht den Wörterbuchwert in Ihrer Klasse haben und das Wörterbuch durch eine Liste ersetzen? – gunr2171

+0

Können Sie angeben, wie die erwartete Ausgabe aussehen soll? Weil ich ziemlich sicher bin, dass komplexe Attributnamen nicht Teil von JSON sind ... – Grx70

Antwort

7

Dies sollte den Trick:

Serialisierung:

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

von expected.ToArray() rufen Sie an eine Reihe von KeyValuePair<MyClass, object> Objekte anstatt das Wörterbuch sind die Serialisierung.

Deserialisierung:

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

Hier deserialisieren Sie das Array und dann das Wörterbuch mit .ToDictionary(...) Anruf abzurufen.

Ich bin mir nicht sicher, ob die Ausgabe Ihre Erwartungen erfüllt, aber sicherlich besteht es die Gleichheitserklärung.

4

Die Antwort von Grx70 ist gut - nur eine alternative Lösung hier hinzufügen. Ich stieß auf dieses Problem in einem Web-API-Projekt, wo ich SerializeObject nicht anruft, aber die Serialisierung automatisch geschehen ließ.

Diese benutzerdefinierten JsonConverter basierend auf Brian Rogers' answer to a similar question hat den Trick für mich:

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(); 
    } 
} 

In meinem Fall wurde ich auf eine Klasse eine Dictionary<MyCustomType, int> Eigenschaft Serialisierung wo MyCustomType Eigenschaften hatte wie Name und Id. Dies ist das Ergebnis:

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

Diese Lösung ist ziemlich gut.Können Sie dies erreichen, indem Sie auch eine Implementierung für die ReadJson-Methode bereitstellen? Vielen Dank! – Nikhil

Verwandte Themen