skip to Main Content

Newtonsoft.Json don’t support FrozenDictionary. I know I can write a JsonConverter starting from scratch, but it is difficult for me, and FrozenDictionary is very similar to a Dictionary when serialize/deserialize. Can I tell Newtonsoft.Json to serialize/deserialize it as Dictionary in some simple way?

Or is there another question on here with a FrozenDictionaryJsonConverter already written by someone else?

2

Answers


  1. The problem is FrozenDictionary doesn’t have a new() – personally I’d deal with it by having two properties and dealing with it in your class constructor or something. If you want JSON though

    Foo myFoo = new Foo()
    {
        FrozenDict = new Dictionary<string, string>() { { "Foo", "Bar" } }.ToFrozenDictionary()
    };
    var fooData = JsonConvert.SerializeObject(myFoo);
    
    var settings = new JsonSerializerSettings
    {
        Converters = { new FrozenDictionaryConverter<string, string>() }
    };
    var newFoo = JsonConvert.DeserializeObject<Foo>(fooData, settings);
    
    public class Foo
    {
        public FrozenDictionary<string, string> FrozenDict;
    }
    
    public class FrozenDictionaryConverter<TKey, TValue> : JsonConverter<FrozenDictionary<TKey, TValue>>
    {
        public override void WriteJson(JsonWriter writer, FrozenDictionary<TKey, TValue>? value, Newtonsoft.Json.JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    
        public override FrozenDictionary<TKey, TValue>? ReadJson(JsonReader reader, Type objectType, FrozenDictionary<TKey, TValue>? existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
        {
            var dictionary = serializer.Deserialize<Dictionary<TKey, TValue>>(reader);
            return dictionary?.ToFrozenDictionary();
        }
    }
    

    The above isn’t generic, you need to set the types on your json converter settings, but it will work.

    Personally, if I had that use case – I would do something like this instead (but with better accessors ofc)

    public class Foo
    {
        [JsonIgnore]
        public FrozenDictionary<string, string> MyFrozenDict;
        public Dictionary<string, string> _jsonMyFrozenDict;
        public Foo() {
            MyFrozenDict = _jsonMyFrozenDict?.ToFrozenDictionary();
        }
    }
    
    Login or Signup to reply.
  2. If you’re building a ASP.NET API and want a more generic way to do this, you could use a converter like this:

    public class FrozenDictionaryConverter : JsonConverter
    {
        private static readonly MethodInfo _deserializeMethod = typeof(FrozenDictionaryConverter)
            .GetMethod(nameof(DeserializeFrozenDictionary), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod);
    
        public override bool CanConvert(Type objectType)
        {
            return objectType.IsConstructedGenericType && objectType.GetGenericTypeDefinition() == typeof(FrozenDictionary<,>);
        }
    
        public override bool CanWrite => false;
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var typedDeserializeMethod = _deserializeMethod.MakeGenericMethod(objectType.GetGenericArguments());
            return typedDeserializeMethod.Invoke(null, new object[] { reader, serializer });
        }
    
        private static object DeserializeFrozenDictionary<TKey, TValue>(JsonReader reader, JsonSerializer serializer)
        {
            var dataAsDictionary = serializer.Deserialize<Dictionary<TKey, TValue>>(reader);
            return dataAsDictionary?.ToFrozenDictionary();
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    This uses some reflection to invoke a typed generic method to do the deserialization. It could probably be made faster by caching the constructed method.

    While this is likely going to be marginally slower than a converter that you have to supply the key/value types to, it has the benefit that you can register this as a converter on your JsonSerializerSettings and it will handle any and all FrozenDictionary types within your models, without explicitly having to register a container for each key/value type pairing.

    I want to add that this doesn’t implement the WriteJson method because that is already supported by JSON.NET (presumably through the IDictionary<TKey, TValue> interface), so we don’t have to create our own.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search