DEV Community

Kelly Brown
Kelly Brown

Posted on

Converting Dictionaries in System.Text.Json

Like many out there, I'm a long-time consumer of Newtonsoft.Json. It's simple and powerful, so it surprised me to learn that .NET Core 3 would be introducing its own JSON library. What surprised me more was learning that the man behind Newtonsoft.Json himself would be involved with designing this new library. This new library, System.Text.Json, was built with a specific purpose in mind: performance with JSON-related functionality in ASP.NET Core. It promises reduced allocations and higher throughput.

The design of this new library is somewhat off-putting for many. Right out of the gate, I wondered what happened to JObject, JArray, and JToken. They're just... gone... with no real replacement. The new library has JsonElement, but it's largely a read-only component. It is only created as part of a large parse operation; I can't just create JSON things and glue them together like I could with Newtonsoft.

I spent more time with it and slowly grew accustomed to its design philosophy. I no longer miss JObject because it was a thin wrapper over things I already have in .NET anyway. What is a JSON object really? It's just a Dictionary<string, object>.

var obj = new Dictionary<string, object>
{
    ["id"] = 43,
    ["title"] = "Western Union",
    ["isEnabled"] = true,
    ["tags"] = new string[] {"things", "stuff"}
};

var json = JsonSerializer.Serialize(obj);

Cool. Simple enough. I can also serialize custom objects with arbitrary properties to accomplish the same things. With this in mind, I kind of see the old Newtonsoft classes as crutches more than anything else. They offer feel-good measures for directly crafting JSON objects and arrays. (And I'm not faulting anyone for sticking with that paradigm. It's pretty easy to read.)

However, I was caught off guard when I went to deserialize a dictionary.

var raw = "{\"49fc2162-744a-4a42-b685-ea1e30ce2a2f\": 99}";
var d = JsonSerializer.Deserialize<Dictionary<Guid, int>>(raw);

This code here throws a NotSupportedException. It points out that having a non-string for the TKey is simply unacceptable. This... is a problem. I work with Guid extensively (at home and at work) and often use it as a dictionary key. I'm generally not OK with having to convert JSON to a string dictionary first and then to a non-string dictionary as a second step. It seems I am not alone in experiencing frustration at this.

However, as much as I love to sit and complain about others' hard work, I decided to just do something about it. The documentation for creating your own JSON converters is pretty good. So, I set out to make my own, and I am pleased with the result so far. Now I can do this:

var options = new JsonSerializerOptions();
options.Converters.Add(DictionaryJsonConverterFactory.Default);

var raw = "{\"49fc2162-744a-4a42-b685-ea1e30ce2a2f\": 99}";
var d = JsonSerializer.Deserialize<Dictionary<Guid, int>>(raw, options);

Back in business! If you are interested in my implementation, the code is all here with a small explanation here. It's public domain. Knock yourself out.

Top comments (0)