Utf8JsonでDictionaryのキーにCustom Formatterを利用する

公開:2020/12/04
更新:2020/12/09
2 min読了の目安(約2300字TECH技術記事

はじめに

.NETでJSONを扱うにあたり、Utf8Jsonというライブラリがあります。

UTF-8ネイティブな .NET用のJSONライブラリにはSystem.Text.Jsonもありますが、参照型を扱う場合にデフォルトコンストラクターが必要なことから、私はUtf8Jsonを使うことがあります。

ここではUtf8Json使う場合に、Dictionaryオブジェクトのキーに組み込み型ではない、自作のクラスや構造体を使う方法を紹介したいと思います。

対象の自作クラス

こんなImmutableなデフォルトコンストラクターを持たないクラスや構造体を、Dictionaryのキーに利用します。

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

        public int IntValue { get; }
    }

Custom Formatterを作成する

Utf8Jsonでは独自クラスでJSONのシリアライズを明示的に指定したい場合、IJsonFormatterを実装する必要がありますが、Dictionaryのキーに利用する場合は、IJsonFormatterに追加してIObjectPropertyNameFormatterを実装する必要があります。
EmployeeIdの例では、intのプロパティのみをシリアライズ・デシリアライズしたいので、Formatterを次のように実装します。

このとき、JSONの仕様上、連想配列(Dictionary)のキーは文字列である必要があるため、それ以外での使用箇所とは別のシリアライズ・デシリアライズを利用できるよう、異なるインターフェイス、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());
    }
}

Custom Formatterを利用する

標準のFormatterに追加して、上記のFormatterを利用したい場合、つぎのように実装しましょう。

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

これで次のようなJSONが得られます。

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

以上です。