【C#/.NET8】System.Text.JsonでJSON文字列をオブジェクトにデシリアライズする方法

前回の記事では、.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.DeserializeJsonException をスローします。

例:

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.JsonNewtonsoft.Json より高速ですが、一部の特殊なフォーマット(例:JSON Path、型ヒント付きのポリモーフィズム)には対応していない場合があります。

まとめ

.NET 8 では System.Text.Json を使うことで、非常にシンプルかつ高速にJSONをデシリアライズできます。

  • JsonSerializer.Deserialize<T>() を使って文字列からオブジェクトを生成
  • プロパティ名がcamelCaseでも JsonSerializerOptionsJsonPropertyName を使えば対応可能
  • 配列やネスト構造も簡単にデシリアライズ可能
  • nullチェックと例外処理も忘れずに

標準ライブラリだけでここまでできるのは嬉しいですね。JSON APIとやり取りするアプリケーションを作るなら、ぜひ System.Text.Json を活用しましょう!

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です