Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize JSON Dictionary with StringComparer

I'm trying to serialize/deserialize a dictionary, the problem is that I create the dictionary with a StringComparer.OrdinalIgnoreCase comparer.

Here is a code snippet of the problem I'm experiencing:

var dict = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

dict["x"] = new Dictionary<string, string>();
dict["x"]["y"] = "something";

var serialized = JsonConvert.SerializeObject(dict);

var unSerialized = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(serialized);

Console.WriteLine((dict.Comparer == unSerialized.Comparer ? "Same" : "Different"));

(.NET Fiddle - Try It)

Prints out the following on the console:

Different

Obviously the JSON serializer doesn't serialize the Comparer that I set when I create the dictionary, but the issue is that I can't set the Comparer after the fact since Dictionary<TKey, TValue>.Comparer is read-only.

I'm sure it has to do with some custom JsonSerializerSetting but I can't seem to figure out how to intercept the collection creation and return a dictionary with a different comparer.

like image 907
Ron Beyer Avatar asked Sep 25 '17 20:09

Ron Beyer


1 Answers

It's a bit late probably, but it is possible to extend generated JSON using JsonConverter will be a bit more complex, but more flexible. I've created a sample for the described case, it is not full
.NET Fiddle - Try It
(feel free to extend if you would decide to use it):

public class DictConverter<TValue> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = JToken.ReadFrom(reader);                
        if (objectType == typeof(Dictionary<string, TValue>))
        {
            var comparer = obj.Value<string>("Comparer");
            Dictionary<string, TValue> result;
            if (comparer == "OrdinalIgnoreCase")
            {
                result = new Dictionary<string, TValue>(StringComparer.OrdinalIgnoreCase);
            }
            else
            {
                result = new Dictionary<string, TValue>();
            }
            obj["Comparer"].Parent.Remove();
            serializer.Populate(obj.CreateReader(), result);
            return result;
        }
        return obj.ToObject(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var obj = JObject.FromObject(value);
        if (value is Dictionary<string, TValue>)
        {
            if((value as Dictionary<string, TValue>).Comparer == StringComparer.OrdinalIgnoreCase)
                obj.Add("Comparer", JToken.FromObject("OrdinalIgnoreCase"));
        }
        obj.WriteTo(writer);

    }
}

and usage

var dict = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

dict["x"] = new Dictionary<string, string>();
dict["x"]["y"] = "something";

var serialized = JsonConvert.SerializeObject(dict, 
    new DictConverter<Dictionary<string,string>>());
var unSerialized = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>
    (serialized, new DictConverter<Dictionary<string, string>>());
like image 126
ASpirin Avatar answered Nov 16 '22 22:11

ASpirin