.NET/C# プログラミングにおける話。
お題は以下のとおり。
命題下記のような JSON 文字列が提供されたとする。
{"Foo":"123"}
この JSON 文字列を、以下の C# クラスのオブジェクトに逆シリアル化したい。
public class Bar
{
public decimal Foo { get; set; }
}
ということで、System.Text.Json 名前空間の JsonSerializer クラスを使って以下のように実装したとする。
// 変数 jsonText には上記の JSON 文字列が格納されているとする
var obj = JsonSerializer.Deserialize<Bar>(jsonText);
これを実行すると何が起きるか? JSON 文字列上、プロパティ Foo には "123" という文字列が格納されているのだが、その逆シリアル化対象である Bar クラスの Foo プロパティの型は int 型である。結果、int 型に文字列を収めることはできないということで、下記例外が発生する。
Unhandled exception. System.Text.Json.JsonException:
The JSON value could not be converted to System.Int32.
これを、JSON 文字列上の "123" を、int 型の文字列表現であると解釈して、どうにか int Foo { get; set; } に格納したい、というのが命題だ。
自分が見つけた解決方法は以下の 2 つ。
方法1. Deserialize メソッドに指定するオプションで指定するSystem.Text.Json.JsonSerialization クラスの Deserialize メソッドには、第二引数に逆シリアル化の動作を変更できるオプション指定 (JsonSerializerOptions オブジェクト) を渡すことができる。そのオプションオブジェクトの NumberHandling プロパティに JsonNumberHandling.AllowReadingFromString ("文字列からの読み取りを許す") を指定する。
var obj = JsonSerializer.Deserialize<Bar>(jsonText, options: new()
{
// このオプションを指定する
NumberHandling = JsonNumberHandling.AllowReadingFromString
});
こうすると、Bar クラスの Foo プロパティに格納する元の JSON 文字列が、"123" であっても (あるいは 123 というように数値であっても)、これを int 値であるとしてパースし、Foo プロパティにそのパース後の値を格納してくれることになる。
方法2. プロパティに JsonNumberHandling 属性を付ける逆シリアル化対象のクラスのプロパティに、JsonNumberHandling 属性を付けることで、プロパティごとに逆シリアル化の動作を変更できる。以下のように、Bar クラスの Foo プロパティに、JsonNumberHandling.AllowReadingFromString ("文字列からの読み取りを許す") を引数に指定した JsonNumberHandling 属性を付けると、
public class Bar
{
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public int Foo { get; set; }
}
この Foo プロパティについては、元の JSON 文字列が "123" であっても、これを int 値であるとしてパースし、Foo プロパティにそのパース後の値を格納してくれる。
まとめ以上の方法にて、元の JSON 文字列上の表現が文字列型の値になっていても、int 型のプロパティに逆シリアル化することができた。方法 1 は、逆シリアル化対象のクラス内のすべてのプロパティに作用するのに対し、方法 2 は、個々のプロパティに個別に指定できる。状況や要件に応じて使い分けたいところ。
あと、この JsonNumberHandling.AllowReadingFromString の指定は、逆シリアル化対象のプロパティの型が、int 型に限らず、byte や short、long、float、double、decimal などなど、すべての数値型で有効に機能した。ただし、プロパティの型が int 型なのに、"123.4" のように小数点以下を含む文字列を読ませようとすると、当然のことながら System.Text.Json.JsonException 例外が発生する。
以上
お題は以下のとおり。
命題下記のような JSON 文字列が提供されたとする。
{"Foo":"123"}
この JSON 文字列を、以下の C# クラスのオブジェクトに逆シリアル化したい。
public class Bar
{
public decimal Foo { get; set; }
}
ということで、System.Text.Json 名前空間の JsonSerializer クラスを使って以下のように実装したとする。
// 変数 jsonText には上記の JSON 文字列が格納されているとする
var obj = JsonSerializer.Deserialize<Bar>(jsonText);
これを実行すると何が起きるか? JSON 文字列上、プロパティ Foo には "123" という文字列が格納されているのだが、その逆シリアル化対象である Bar クラスの Foo プロパティの型は int 型である。結果、int 型に文字列を収めることはできないということで、下記例外が発生する。
Unhandled exception. System.Text.Json.JsonException:
The JSON value could not be converted to System.Int32.
これを、JSON 文字列上の "123" を、int 型の文字列表現であると解釈して、どうにか int Foo { get; set; } に格納したい、というのが命題だ。
自分が見つけた解決方法は以下の 2 つ。
方法1. Deserialize メソッドに指定するオプションで指定するSystem.Text.Json.JsonSerialization クラスの Deserialize メソッドには、第二引数に逆シリアル化の動作を変更できるオプション指定 (JsonSerializerOptions オブジェクト) を渡すことができる。そのオプションオブジェクトの NumberHandling プロパティに JsonNumberHandling.AllowReadingFromString ("文字列からの読み取りを許す") を指定する。
var obj = JsonSerializer.Deserialize<Bar>(jsonText, options: new()
{
// このオプションを指定する
NumberHandling = JsonNumberHandling.AllowReadingFromString
});
こうすると、Bar クラスの Foo プロパティに格納する元の JSON 文字列が、"123" であっても (あるいは 123 というように数値であっても)、これを int 値であるとしてパースし、Foo プロパティにそのパース後の値を格納してくれることになる。
方法2. プロパティに JsonNumberHandling 属性を付ける逆シリアル化対象のクラスのプロパティに、JsonNumberHandling 属性を付けることで、プロパティごとに逆シリアル化の動作を変更できる。以下のように、Bar クラスの Foo プロパティに、JsonNumberHandling.AllowReadingFromString ("文字列からの読み取りを許す") を引数に指定した JsonNumberHandling 属性を付けると、
public class Bar
{
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public int Foo { get; set; }
}
この Foo プロパティについては、元の JSON 文字列が "123" であっても、これを int 値であるとしてパースし、Foo プロパティにそのパース後の値を格納してくれる。
まとめ以上の方法にて、元の JSON 文字列上の表現が文字列型の値になっていても、int 型のプロパティに逆シリアル化することができた。方法 1 は、逆シリアル化対象のクラス内のすべてのプロパティに作用するのに対し、方法 2 は、個々のプロパティに個別に指定できる。状況や要件に応じて使い分けたいところ。
あと、この JsonNumberHandling.AllowReadingFromString の指定は、逆シリアル化対象のプロパティの型が、int 型に限らず、byte や short、long、float、double、decimal などなど、すべての数値型で有効に機能した。ただし、プロパティの型が int 型なのに、"123.4" のように小数点以下を含む文字列を読ませようとすると、当然のことながら System.Text.Json.JsonException 例外が発生する。
以上