Use Custom Formatter for Dictionary key in Utf8Json

1 minute read

Introduction

There is a library called Utf8Json for handling JSON in .NET.

System.Text.Json, But I sometimes use Utf8Json because I need a default constructor when dealing with reference types.

Here, when using Utf8Json, I would like to introduce how to use a self-made class or structure that is not a built-in type for the key of the Dictionary object.

Target self-made class

Use a class or structure that does not have such an Immutable default constructor for the dictionary key.

    public readonly struct EmployeeId
    {
        public EmployeeId(int intValue)
        {
            IntValue = intValue;
        }

        public int IntValue { get; }
    }

Implement Custom Formatter

In Utf8Json, if you want to explicitly specify JSON serialization in your own class, you need to implement IJsonFormatter, but if you want to use it for Dictionary key, you need to implement IObjectPropertyNameFormatter in addition to IJsonFormatter.
In the EmployeeId example, we want to serialize and deserialize only the int property, so we implement Formatter as follows:

At this time, the key of the associative array (Dictionary) needs to be a character string according to the JSON specification (Thanks to @ktz_alias for pointing it out!), So we will implement the conversion with a different interface IObjectPropertyNameFormatter.

public sealed class EmployeeIdFormatter : IJsonFormatter<EmployeeId>, IObjectPropertyNameFormatter<EmployeeId>
{
    public void Serialize(ref JsonWriter writer, EmployeeId value, IJsonFormatterResolver formatterResolver)
    {
        writer.WriteInt32(value.IntValue);
    }

    public EmployeeId Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
    {
        return new EmployeeId(reader.ReadInt32());
    }

    public void SerializeToPropertyName(ref JsonWriter writer, EmployeeId value, IJsonFormatterResolver formatterResolver)
    {
        writer.WriteInt32(value.IntValue);
    }

    public EmployeeId DeserializeFromPropertyName(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
    {
        return new EmployeeId(reader.ReadString());
    }
}

Use Custom Formatter

If you want to use the above Formatter in addition to the standard Formatter, implement it as follows.

CompositeResolver.RegisterAndSetAsDefault(
    new IJsonFormatter[] {new EmployeeIdFormatter()},
    new[] {StandardResolver.Default});

var employeeNames = new Dictionary<EmployeeId, string>
{
    [new EmployeeId(0)] = "Steve Jobs", 
    [new EmployeeId(1)] = "Bill Gates"
};
var jsonBytes = Utf8Json.JsonSerializer.Serialize(employeeNames);

This will give you the following JSON.

{"0":"Steve Jobs","1":"Bill Gates"}

that’s all.