C# .Net で Json 形式で書かれたファイルを扱う際は、2022 年現在だと System.Text.Json を利用するのがもっとも手軽だし、一般的だと思います。
このあたりと基本的な使い方については、以下の記事で記述しています。
今回は System.Text.Json でインターフェースを利用したプロパティをデシリアライズするためにコンバーターを利用する方法をメモしました。
インターフェースをデシリアライズする
public void Sample1() { ISample iSample = new Sample(1, 2, "test"); var options = new JsonSerializerOptions { WriteIndented = true, Converters = { // まだ未設定 } }; // シリアライズは型でエラーにならない var jsonText = JsonSerializer.Serialize(iSample, options); // デシリアライズはインターフェースを解決できないため、エラーになる var dSample = JsonSerializer.Deserialize<ISample>(jsonText, options); }
System.NotSupportedException: 'Deserialization of interface types is not supported.
インターフェース型はサポートしていないという例外が出力されました。しかし、ここで変換をサポートするコンバーターを追加してやれば、変換できるようになります。
using System.Text.Json; using System.Text.Json.Serialization; public class InterfaceConverter<TInterface, TImplement> : JsonConverter<TInterface> where TImplement : TInterface { public override TInterface? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return (TInterface?)JsonSerializer.Deserialize(ref reader, typeof(TImplement), options); } public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, typeof(TImplement), options); } }
TInterface
を TImplement
型のクラスに変換するコンバーターです。利用したコードは以下のとおり:
public void Sample2() { ISample iSample = new Sample(3, 4, "test2"); var options = new JsonSerializerOptions { WriteIndented = true, Converters = { new InterfaceConverter<ISample, Sample>() } }; var jsonText = JsonSerializer.Serialize(iSample, options); var dSample = JsonSerializer.Deserialize<ISample>(jsonText, options); }
デシリアライズの型を明示しなくても、デシリアライズできるようになりました。
クラスのパラメーターにジェネリック型を含む
以下のクラスの場合はどうでしょうか。
internal class SampleGenerics<T> where T : struct { public T Value1 { get; set; } public T Value2 { get; set; } public SampleGenerics(T value1, T value2) { Value1 = value1; Value2 = value2; } }
この場合は特に、コンバーターは必要ありません。デシリアライズをするときに型を指定しているためですね。(例のように int
を double
に変換することもできます)
public void Sample3() { var sample = new SampleGenerics<int>(5, 6); var options = new JsonSerializerOptions { WriteIndented = true, Converters = { } }; var jsonText = JsonSerializer.Serialize(sample, options); var dSample = JsonSerializer.Deserialize<SampleGenerics<double>>(jsonText, options); }
なので、コンバーターでインターフェースをデシリアライズするケースは、そんなに多くないと思います。むしろ、ちょっと特殊なケースが殆どだと思います。
デシリアライズで指定する型だけでは処理しきれなくなったとき、コンバーターの利用すれば丁度よいときがあるので検討しよう。