Jørgen Austvik Jørgen Austvik - 26 days ago 25x
C# Question

Newtonsoft Json Deserialize Dictionary as Key/Value list from DataContractJsonSerializer

I have a dictionary serialized to storage with DataContractJsonSerializer which I would like to deserialize with Newtonsoft.Json.

The DataContractJsonSerializer has serialized the Dictionary to a list of Key/Value pairs:


Is there any cool options I can give the
that will make it support both that data format and the format from Newtonsoft.Json?


Is the pretty format Newtonsoft.Json creates, and I would like to be able to read both the old DataContract format and the new Newtonsoft format in a transition period.

Simplified example:

public sealed class Data
public IDictionary<string, string> Dict { get; set; }

public void TestSerializeDataContractDeserializeNewtonsoftDictionary()
var d = new Data
Dict = new Dictionary<string, string>
{"Key1", "Val1"},
{"Key2", "Val2"},

var oldJson = String.Empty;
var formatter = new DataContractJsonSerializer(typeof (Data));
using (var stream = new MemoryStream())
formatter.WriteObject(stream, d);
oldJson = Encoding.UTF8.GetString(stream.ToArray());

var newJson = JsonConvert.SerializeObject(d);
// [JsonArray] on Data class gives:
// System.InvalidCastException: Unable to cast object of type 'Data' to type 'System.Collections.IEnumerable'.

// This is tha data I have in storage and want to deserialize with Newtonsoft.Json, an array of key/value pairs
// {"Dict":[{"Key":"Key1","Value":"Val1"},{"Key":"Key2","Value":"Val2"}]}

// This is what Newtonsoft.Json generates and should also be supported:
// {"Dict":{"Key1":"Val1","Key2":"Val2"}}

var d2 = JsonConvert.DeserializeObject<Data>(newJson);
Assert.AreEqual("Val1", d2.Dict["Key1"]);
Assert.AreEqual("Val2", d2.Dict["Key2"]);

var d3 = JsonConvert.DeserializeObject<Data>(oldJson);
// Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into
// type 'System.Collections.Generic.IDictionary`2[System.String,System.String]' because the type requires a JSON
// object (e.g. {"name":"value"}) to deserialize correctly.
// To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type
// to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be
// deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from
// a JSON array.
// Path 'Dict', line 1, position 9.

Assert.AreEqual("Val1", d3.Dict["Key1"]);
Assert.AreEqual("Val2", d3.Dict["Key2"]);


You could use a custom converter for this, depending on what token the dictionary starts with, deserialize it JSON.NET's default way, or deserialize it into an array and then turn that array into a Dictionary:

public class DictionaryConverter : JsonConverter
    public override object ReadJson(
        JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
        IDictionary<string, string> result;

        if (reader.TokenType == JsonToken.StartArray)
            JArray legacyArray = (JArray)JArray.ReadFrom(reader);

            result = legacyArray.ToDictionary(
                el => el["Key"].ToString(),
                el => el["Value"].ToString());
            result = 
                (IDictionary<string, string>)
                    serializer.Deserialize(reader, typeof(IDictionary<string, string>));

        return result;

    public override void WriteJson(
        JsonWriter writer, object value, JsonSerializer serializer)
        throw new NotImplementedException();

    public override bool CanConvert(Type objectType)
        return typeof(IDictionary<string, string>).IsAssignableFrom(objectType);

    public override bool CanWrite 
        get { return false; } 

Then, you can decorate the Dict property in the Data class with a JsonConverter attribute:

public sealed class Data
    public IDictionary<string, string> Dict { get; set; }

Then deserializing both strings should work as expected.