Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I serialize/deserialize a dictionary with custom keys using Json.Net?

I have the following class, that I use as a key in a dictionary:

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

The test I am running is here:

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

The test fails, because Json.Net seems to be using the ToString() method on the dictionary keys, instead of serializing them properly. The resulting json from the test above is:

{
  "$type": "System.Collections.Generic.Dictionary`2[[RiskAnalytics.UnitTests.API.TestMarketContainerSerialisation+MyClass, RiskAnalytics.UnitTests],[System.Object, mscorlib]], mscorlib",
  "RiskAnalytics.UnitTests.API.TestMarketContainerSerialisation+MyClass": 5.2
}

which is clearly wrong. How can I get it to work?

like image 234
Grzenio Avatar asked Jul 10 '14 16:07

Grzenio


People also ask

Can JSON serialize dictionary?

Json can't serialize Dictionary unless it has a string key. The built-in JSON serializer in . NET Core can't handle serializing a dictionary unless it has a string key.

Can you serialize a dictionary C#?

NET objects is made easy by using the various serializer classes that it provides. But serialization of a Dictionary object is not that easy. For this, you have to create a special Dictionary class which is able to serialize itself. The serialization technique might be different in different business cases.

How does Newtonsoft JSON deserialize work?

Newtonsoft. Json uses reflection to get constructor parameters and then tries to find closest match by name of these constructor parameters to object's properties. It also checks type of property and parameters to match. If there is no match found, then default value will be passed to this parameterized constructor.

What is the difference between serialize and deserialize JSON?

JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object). If you serialize this result it will generate a text with the structure and the record returned.


2 Answers

Simpler, full solution, using a custom JsonConverter

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;

public class CustomDictionaryConverter<TKey, TValue> : JsonConverter
{
    public override bool CanConvert(Type objectType) => objectType == typeof(Dictionary<TKey, TValue>);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        => serializer.Serialize(writer, ((Dictionary<TKey, TValue>)value).ToList());

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        => serializer.Deserialize<KeyValuePair<TKey, TValue>[]>(reader).ToDictionary(kv => kv.Key, kv => kv.Value);
}

Usage:

[JsonConverter(typeof(CustomDictionaryConverter<KeyType, ValueType>))]
public Dictionary<KeyType, ValueType> MyDictionary;
like image 66
Gerardo Marset Avatar answered Nov 02 '22 00:11

Gerardo Marset


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.

like image 36
Grx70 Avatar answered Nov 02 '22 00:11

Grx70