前回の記事では、.NET 8 における標準ライブラリ System.Text.Json
を使って、オブジェクトをJSON文字列にシリアライズする方法をご紹介しました。
今回はその逆、JSON文字列をC#のオブジェクトに変換する「デシリアライズ」の方法について詳しく解説していきます。
たとえば、Web APIから受け取ったJSONレスポンスを自分のアプリケーションで扱いやすくするには、JSON文字列をC#のクラスに変換する必要があります。そのときに使えるのが、JsonSerializer.Deserialize<T>()
メソッドです。
基本のデシリアライズ:JSONをオブジェクトに変換する
まず、基本の Person
クラスから復習です:
public record Person { public int Id { get; set; } public string Name { get; set; } }
このクラスに対応するJSON文字列が以下のようにあるとしましょう。
{"Id":1,"Name":"Taro"}
これをC#でデシリアライズするには、以下のように書きます。
using System.Text.Json; string json = "{\"Id\":1,\"Name\":\"Taro\"}"; Person? person = JsonSerializer.Deserialize<Person>(json); Console.WriteLine($"{person?.Id}, {person?.Name}");
出力:
1, Taro
非常に簡単ですね。JSON文字列が有効なフォーマットで、対応するクラスが存在すれば、すぐにデシリアライズできます。
プロパティ名が小文字(camelCase)のJSONをデシリアライズする
JavaScriptなど他言語では、プロパティ名が小文字(camelCase)であることが多いです。以下のようなJSON:
{"id":2,"name":"Jiro"}
の場合、C#のクラスではPascalCase(Id, Name)で定義しているので、そのままではプロパティにマッピングされません。
このような場合は、JsonSerializerOptions
を使ってネーミングポリシーを設定します。
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true // 大文字小文字を無視 }; string json = "{\"id\":2,\"name\":\"Jiro\"}"; var person = JsonSerializer.Deserialize<Person>(json, options); Console.WriteLine($"{person?.Id}, {person?.Name}");
出力:
2, Jiro
これで、camelCaseなJSON文字列にも柔軟に対応できます。
配列のデシリアライズ
複数の Person
オブジェクトが入ったJSON配列をデシリアライズすることも可能です。
以下のJSONを用意します:
[ {"Id":1,"Name":"Taro"}, {"Id":2,"Name":"Jiro"} ]
この配列をデシリアライズしてみましょう。
string jsonArray = "[{\"Id\":1,\"Name\":\"Taro\"},{\"Id\":2,\"Name\":\"Jiro\"}]"; var people = JsonSerializer.Deserialize<Person[]>(jsonArray); foreach (var p in people!) { Console.WriteLine($"{p.Id}, {p.Name}"); }
出力:
1, Taro 2, Jiro
もちろん、List<Person>
として受け取ることも可能です:
List<Person>? peopleList = JsonSerializer.Deserialize<List<Person>>(jsonArray);
ネストされたオブジェクトのデシリアライズ
次に、オブジェクトの中に配列があるような、入れ子構造のJSONの例を見てみましょう。
{ "GroupId": 1, "People": [ { "Id": 1, "Name": "Taro" }, { "Id": 2, "Name": "Jiro" } ] }
このJSONに対応する Group
クラスを以下のように定義します:
public record Group { public int GroupId { get; set; } public Person[] People { get; set; } }
そして、デシリアライズします。
string json = @" { ""GroupId"": 1, ""People"": [ { ""Id"": 1, ""Name"": ""Taro"" }, { ""Id"": 2, ""Name"": ""Jiro"" } ] }"; var group = JsonSerializer.Deserialize<Group>(json); Console.WriteLine($"Group ID: {group?.GroupId}"); foreach (var p in group?.People!) { Console.WriteLine($"- {p.Id}, {p.Name}"); }
出力:
Group ID: 1 - 1, Taro - 2, Jiro
このように、入れ子になった構造でも、クラス構造を合わせれば問題なくデシリアライズできます。
JsonPropertyName属性を使ったマッピング
シリアライズと同じように、デシリアライズ時にも [JsonPropertyName]
属性を使ってJSONとクラスのプロパティ名をマッピングできます。
public record Person { [JsonPropertyName("id")] public int Id { get; set; } [JsonPropertyName("name")] public string Name { get; set; } }
JSONが {"id":3,"name":"Saburo"}
のようになっていても、以下のようにスムーズに読み取れます:
string json = "{\"id\":3,\"name\":\"Saburo\"}"; var person = JsonSerializer.Deserialize<Person>(json); Console.WriteLine($"{person?.Id}, {person?.Name}");
デシリアライズの失敗と例外処理
もしJSONが不正な形式だったり、クラスとマッピングが合っていなかった場合、JsonSerializer.Deserialize
は JsonException
をスローします。
例:
string invalidJson = "{\"Id\": \"NotAnInt\", \"Name\": \"Taro\"}"; try { var person = JsonSerializer.Deserialize<Person>(invalidJson); } catch (JsonException ex) { Console.WriteLine("デシリアライズに失敗しました: " + ex.Message); }
nullの扱いに注意
Deserialize<T>
は、デフォルトで null
を返す可能性があります。値を安全に扱うためには null
チェックを必ず行いましょう。
パフォーマンスとセキュリティの注意点
- デシリアライズ対象のJSONは信頼できるソースからのみ受け取るべきです。攻撃者が意図的に破壊的なJSONを送信してくるケースがあります。
System.Text.Json
はNewtonsoft.Json
より高速ですが、一部の特殊なフォーマット(例:JSON Path、型ヒント付きのポリモーフィズム)には対応していない場合があります。
まとめ
.NET 8 では System.Text.Json
を使うことで、非常にシンプルかつ高速にJSONをデシリアライズできます。
JsonSerializer.Deserialize<T>()
を使って文字列からオブジェクトを生成- プロパティ名がcamelCaseでも
JsonSerializerOptions
やJsonPropertyName
を使えば対応可能 - 配列やネスト構造も簡単にデシリアライズ可能
- nullチェックと例外処理も忘れずに
標準ライブラリだけでここまでできるのは嬉しいですね。JSON APIとやり取りするアプリケーションを作るなら、ぜひ System.Text.Json
を活用しましょう!