はじめに
.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)のキーは文字列である必要があるため( @ktz_alias さんに指摘いただきました。ありがとうございました!)、異なるインターフェース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"}
以上です。