C# で JSON を扱うときは、単純に「System.Text.Json」ケースが増えてきました。DataContractJsonSerializer
や Newtonsoft.Json
のように他の選択肢もあるけど、徐々に移行されていくかと。
「System.Text.Json」を利用するケースで、問題になることがあった例のひとつに「非数値 (NaN) の扱い」というものがありました。標準では、非数値を含むクラスなんかのシリアライズは、エラーになってしまう(JSON のフォーマットの問題)ので、独自に JsonConverter<T>
を設計する必要がありました。
「.NET number values such as positive and negative infinity cannot be written as valid JSON」というエラーになります。
こうしたケースだと、 Newtonsoft.Json
のほうが、少しだけコンバーターを楽に設計できたりしたのですが、「System.Text.Json」のバージョンが5になって非数値に対応できるようになっていました。
待望の機能だったので、とりあえずやってみます。
ただし、まだ System.Text.Json 5 は RC (Release Candidate: リリース候補) の状態です。安定版ではないので注意。
書き込みのテストコード(エラー発生)
こんな感じで、NaN
を含めるとエラーになります。ここまでは、今まで通り。
public class ClassWithInts { public int NumberOne { get; set; } public double NumberTwo { get; set; } } public void Run() { var data = new ClassWithInts { NumberOne = -1, NumberTwo = double.NaN, }; var json = JsonSerializer.Serialize(data); }
書き込みのテストコード
System.Text.Json
は 5.0 になって、JsonSerializerOptions
に NumberHandling
プロパティが追加されています。シリアライズするときの処置を補足できます。
以下のようにしてみます。
public void Run() { var options = new JsonSerializerOptions { NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals, WriteIndented = true, }; var data = new ClassWithInts { NumberOne = -1, NumberTwo = double.NaN, NumberPositive = double.PositiveInfinity, NumberNegative = double.NegativeInfinity, }; var json = JsonSerializer.Serialize(data, options); Console.WriteLine(json); }
無事、出力できるようになりました。
読み込みのテストコード(エラー発生)
書き込みと同じで、デフォルトの状態だとエラーが発生します。ここまでは、今まで通り。
public void ReadTest(string json) { // var json = @"{""NumberOne"":-1,""NumberTwo"":""NaN"",""NumberPositive"":""Infinity"",""NumberNegative"":""-Infinity""}"; var data = JsonSerializer.Deserialize<ClassWithInts>(json, options); }
読み込みのテストコード
書き込みと一緒で、JsonSerializerOptions
に NumberHandling
プロパティを設定して、シリアライズするときの処置を補足します。
public void ReadTest(string json) { var options = new JsonSerializerOptions { NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals, }; var data = JsonSerializer.Deserialize<ClassWithInts>(json, options); }
読み込みできるようになりました。コンバーターいらずなので、すごく楽。
シリアライズのオプションの意味
追加された JsonNumberHandling
のコメントはこんな感じになっています。
JsonNumberHandling.AllowReadingFromString
Numbers can be read fromSystem.Text.Json.JsonTokenType.String
tokens Does not prevent numbers from being read fromSystem.Text.Json.JsonTokenType.Number
token.JsonNumberHandling.AllowNamedFloatingPointLiterals
The "NaN", "Infinity", and "-Infinity"System.Text.Json.JsonTokenType.String
tokens can be read as floating-point constants, and theSystem.Single
andSystem.Double
values for these constants will be written as their corresponding JSON string representations.
ざっくり訳:
JsonNumberHandling.AllowReadingFromString
System.Text.Json.JsonTokenType.String
トークンから、数値を読み取るようにします。ただし、System.Text.Json.JsonTokenType.Number
からの数値の読み取りはできません。JsonNumberHandling.AllowNamedFloatingPointLiterals
System.Text.Json.JsonTokenType.String
トークンから "NaN"、"Infinity"、"-Infinity" の値を浮動小数の定数として読み込むことができるようになります。System.Single
とSystem.Double
の定数は、対応する JSON 文字列表現として書き込みされます。
読み取りのときは
JsonNumberHandling.AllowReadingFromString
を設定しなくても成功しましたが、提案時点ではつける方針だったみたいなので設定しています。
サンプル
GitHub の「Sample」の中に「TextJsonNaN」のソリューションを追加しました。
参照
- 作者:BillWagner
- 発売日: 2018/09/05
- メディア: Kindle版