How can I serialize/deserialize a dictionary with custom keys using Json.Net?
Grx70's answer is good - just adding an alternative solution here. I ran into this problem in a Web API project where I wasn't calling SerializeObject
but allowing the serialization to happen automagically.
This custom JsonConverter
based on Brian Rogers' answer to a similar question did the trick for me:
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 my case, I was serializing a Dictionary<MyCustomType, int>
property on a class where MyCustomType
had properties like Name
and Id
. This is the result:
...
"dictionaryProp": [
[
{
"name": "MyCustomTypeInstance1.Name",
"description": null,
"id": null
},
3
],
[
{
"name": "MyCustomTypeInstance2.Name",
"description": null,
"id": null
},
2
]
]
...
This should do the trick:
Serialization:
JsonConvert.SerializeObject(expected.ToArray(), Formatting.Indented, jsonSerializerSettings);
By calling expected.ToArray()
you're serializing an array of KeyValuePair<MyClass, object>
objects rather than the dictionary.
Deserialization:
JsonConvert.DeserializeObject<KeyValuePair<IDataKey, object>[]>(output, jsonSerializerSettings).ToDictionary(kv => kv.Key, kv => kv.Value);
Here you deserialize the array and then retrieve the dictionary with .ToDictionary(...)
call.
I'm not sure if the output meets your expectations, but surely it passes the equality assertion.