Json.NET and dictionaries

TL;DR Json.NET can serialize and deserialize dictionaries with simple keys like integers or strings. It can serialize, but not deserialize dictionaries with more complex keys.

Longer version: Json.NET uses ToString() to convert a dictionary key to JSON field identifier. A simple case works like this:

            var dict = new Dictionary<int, string>
            {
                {10, "ten"},
                {42, "forty two"}
            };

            var json = JsonConvert.SerializeObject(dict);
            Console.WriteLine(json); // prints {"10":"ten","42":"forty two"}

            var deserialized = JsonConvert.DeserializeObject<Dictionary<int, string>>(json); // gets your dictionary back

The dictionary value can be of any complex type: normal serialization rules apply.

            var dict = new Dictionary<int, Tuple<int, string>>()
            {
                {10, Tuple.Create(10, "ten")},
                {42, Tuple.Create(42, "forty two")},
            };

            Console.WriteLine();
            var json = JsonConvert.SerializeObject(dict);
            Console.WriteLine(json); // {"10":{"Item1":10,"Item2":"ten"},"42":{"Item1":42,"Item2":"forty two"}}

            var deserialized = JsonConvert.DeserializeObject<Dictionary<int, Tuple<int, string>>>(json);

However, Json.NET uses ToString() to generate dictionary keys:

            var dict = new Dictionary<Tuple<int, int>, string>
            {
                { Tuple.Create(10,20), "ten-twenty" },
                { Tuple.Create(5,3), "five-three" },
            };

            var json = JsonConvert.SerializeObject(dict);
            Console.WriteLine(json); // {"(10, 20)":"ten-twenty","(5, 3)":"five-three"}

            var deserialized = JsonConvert.DeserializeObject<Dictionary<Tuple<int, int>, string>>(json); // blows up 

It is even possible to trick Json.NET into generating duplicate keys (which appears to be technically valid JSON):

        struct CustomKey
        {
            private readonly int _val;

            public CustomKey(int val)
            {
                _val = val;
            }

            public override string ToString()
            {
                return "CustomKey";
            }

            public override int GetHashCode()
            {
                return _val.GetHashCode();
            }

            public override bool Equals(object obj)
            {
                if (!(obj is CustomKey)) return false;
                return _val == ((CustomKey)obj)._val;
            }
        }

            var dict = new Dictionary<CustomKey, string>
            {
                {new CustomKey(10), "ten"},
                {new CustomKey(42), "forty two"}
            };

            var json = JsonConvert.SerializeObject(dict);
            Console.WriteLine(json); // {"CustomKey":"ten","CustomKey":"forty two"}

            var deserialized = JsonConvert.DeserializeObject<Dictionary<CustomKey, string>>(json); // blows up

Well, no one is perfect 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *