【C#】System.Text.Json と Newtonsoft.Json のデシリアライズ時のJSONの値の型の扱いについて
Newtonsoft.Json を使っていたプロジェクトを、System.Text.Json に書き換えているときに見つけた挙動の差についてのメモです。
デシリアライズするときに食わせるJSONの、値の書き方と、モデルの型とのバリデーションの挙動が異なっていました。
違い
以下のコードは、NumericValueA、NumericValueBのプロパティを持つモデルへと、数値データを持っているJSON文字列をデシリアライズするものです。
この時、モデルは、NumericValueA(string型)、NumericValueB(long型)ですが、
食わせるJSONは、NumericValueA(数値)、NumericValueB(文字列)にしてみます。
public sealed class SampleModel
{
public required string NumericValueA { get; init; }
public required long NumericValueB { get; init; }
}
static async Task MainAsync(string[] args)
{
const string sampleJson = @"{""NumericValueA"":1234, ""NumericValueB"":""5678""}";
Console.WriteLine("- System.Text.Json - ");
try
{
var data = System.Text.Json.JsonSerializer.Deserialize<SampleModel>(sampleJson);
Console.Write($"NumericValueA : {data.NumericValueA} , NumericValueB : {data.NumericValueB}");
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
Console.WriteLine("");
Console.WriteLine("- Newtonsoft.Json - ");
try
{
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<SampleModel>(sampleJson);
Console.Write($"NumericValueA : {data.NumericValueA} , NumericValueB : {data.NumericValueB}");
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
}
このコードを実行したらこうなりました
- System.Text.Json -
The JSON value could not be converted to System.String. Path: $.NumericValueA | LineNumber: 0 | BytePositionInLine: 21.
- Newtonsoft.Json -
NumericValueA : 1234 , NumericValueB : 5678
System.Text.Json
は、string 型なのにJSONが数値になっているので、変換できんと言って例外を吐いています。
Newtonsoft.Json
は、なんか良しなに埋め込んで、いい感じに変換してくれています()
つまり、System.Text.Json
のほうが厳密にデシリアライズしてくれているようです。
System.Text.Json のオプション
System.Text.Json.JsonSerializer.Deserialize
の第二引数には、```System.Text.Json.JsonSerializerOptions`` を渡せます。
結構細かく設定ができるので、どうしても互換性を保ってNewtonsoft.Json
からSystem.Text.Json
に書き換えるときは、JsonSerializerOptions
をなるべく寄せて設定するといいかもしれません。
↓JsonSerializerOptions
のドキュメントに、各プロパティの説明が書いてあります。
・・・
でも。。。NumberHandling
位しか、有用そうなオプションがないような。。。
一応、以下のようにすると、JSONの値が文字列だった時に、数値型のC#プロパティにコンバートして格納してくれます。
探したけど、JSONの値が数値の時に、文字列型のC#プロパティにコンバートして格納してくれるようなオプションはなさそうでした。
const string sampleJson = @"{""NumericValue"":""1234""}";
var oprtion = new JsonSerializerOptions
{
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString
};
var data = System.Text.Json.JsonSerializer.Deserialize<SampleModel>(sampleJson, oprtion);
Console.Write($"NumericValue : {data.NumericValue}");
Discussion