C# JObjectの使い方完全ガイド|JSONの解析・取得・追加・更新を初心者向けに解説

はじめに

C#でJSONを扱うときによく使われるのが、Newtonsoft.JsonのJObjectです。APIレスポンス、設定ファイル、ログデータ、外部サービスとの連携など、C#アプリケーションではJSONを読み書きする場面が多くあります。

JObjectを使うと、JSON文字列をC#のオブジェクトとして読み込み、プロパティの値を取得したり、追加・更新・削除したりできます。特に「JSONの構造が固定されていない」「一部の値だけ取り出したい」「C#クラスを作るほどではない」といった場面で便利です。

この記事では、C#のJObjectについて、基本的な使い方から実践的なJSON操作、よくあるエラーの対処法まで初心者向けに解説します。

1. C#のJObjectとは?JSONを扱うための基本

1-1. JObjectはNewtonsoft.JsonでJSONオブジェクトを表すクラス

JObjectは、Newtonsoft.Json、通称Json.NETに含まれるクラスで、JSONの「オブジェクト」をC#上で表現するために使います。Newtonsoft.Jsonの公式ドキュメントでも、JObjectはJSONオブジェクトを表すクラスとして説明されています。Newtonsoft

たとえば、次のJSONはオブジェクトです。

JSON
{
"name": "Taro",
"age": 30,
"isActive": true
}

このJSONをC#でJObjectとして扱うと、nameageなどのプロパティにアクセスできます。

C#
using Newtonsoft.Json.Linq;

string json = @"{
""name"": ""Taro"",
""age"": 30,
""isActive"": true
}";

JObject obj = JObject.Parse(json);

Console.WriteLine(obj["name"]);
Console.WriteLine(obj["age"]);
Console.WriteLine(obj["isActive"]);

実行結果は次のようになります。

Taro
30
True

JObjectは、JSONを辞書のようにキーで取り出せるため、初心者でも直感的に扱いやすいのが特徴です。

1-2. JObject・JToken・JArray・JValueの違い

Newtonsoft.Jsonでは、JSONを扱うために複数のクラスが用意されています。特によく使うのが、JObjectJTokenJArrayJValueです。

JObjectはJSONオブジェクトを表します。

JSON
{
"name": "Taro",
"age": 30
}

JArrayはJSON配列を表します。公式ドキュメントでも、JArrayは要素を追加でき、JPath式でトークンを選択できるクラスとして説明されています。Newtonsoft

JSON
[
"C#",
"JavaScript",
"Python"
]

JValueは文字列、数値、真偽値、nullなどの単一の値を表します。

JSON
"Taro"

JTokenは、これらの親のような存在です。JObjectJArrayJValueはいずれもJTokenとして扱えます。

イメージとしては、次のように考えると分かりやすいです。

JToken
├─ JObject // JSONオブジェクト
├─ JArray // JSON配列
└─ JValue // 文字列・数値・bool・nullなどの値

たとえば、次のJSONを見てみましょう。

JSON
{
"user": {
"name": "Taro",
"age": 30
},
"tags": ["csharp", "json"],
"active": true
}

この場合、全体はJObjectuserJObjecttagsJArraynameageactiveJValueとして扱えます。

1-3. JObjectを使うメリットと利用シーン

JObjectを使うメリットは、JSONの構造を柔軟に扱えることです。

通常、JSONをC#クラスに変換する場合は、あらかじめ次のようなクラスを用意します。

C#
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}

しかし、APIレスポンスの項目が変わる場合や、一部の値だけ取り出したい場合は、毎回クラスを作るのが面倒です。そのようなときにJObjectが便利です。

主な利用シーンは次のとおりです。

  • APIレスポンスから一部の値だけ取得したい

  • JSONの構造が動的に変わる

  • 設定ファイルの値を読み込みたい

  • JSONの一部を追加・更新・削除したい

  • デバッグや検証のためにJSONを一時的に扱いたい

特に、外部APIのレスポンスを扱うときは、必要な値だけをSelectTokenやプロパティ名で取得できるため、素早く実装できます。

1-4. System.Text.Jsonとの違い

C#でJSONを扱う方法には、Newtonsoft.Jsonのほかに、Microsoftが提供するSystem.Text.Jsonがあります。System.Text.Jsonは.NET Core 3.1以降のランタイムに含まれており、JSONのシリアライズとデシリアライズを行うための標準的な機能として利用できます。Microsoft Learn

一方、JObjectはNewtonsoft.Jsonのクラスです。つまり、System.Text.JsonにはJObjectというクラスはありません。System.Text.Jsonで似たようなDOM操作を行う場合は、JsonDocumentJsonElementJsonNodeJsonObjectなどを使います。Microsoftのドキュメントでも、System.Text.JsonでJSON DOMを使ってJSONペイロードへランダムアクセスする方法が紹介されています。Microsoft Learn

大まかな使い分けは次のようになります。

Newtonsoft.JsonのJObject
→ 柔軟にJSONを操作したい
→ 既存プロジェクトでJson.NETを使っている
→ SelectTokenなどでパス指定したい
→ 複雑なJSONを扱いたい

System.Text.Json
→ .NET標準機能を使いたい
→ パフォーマンスを重視したい
→ 新規プロジェクトで標準ライブラリ中心にしたい

また、JSONの解釈にも違いがあります。たとえば、Newtonsoft.Jsonは一部のゆるいJSON表現を受け入れる場合がありますが、System.Text.Jsonは仕様に沿った厳密なJSONを前提にしています。Microsoftの移行ドキュメントでも、System.Text.Jsonでは二重引用符で囲まれたプロパティ名と文字列値のみが有効なJSONとして扱われることが説明されています。Microsoft Learn

2. JObjectを使うための準備

2-1. Newtonsoft.Jsonのインストール方法

JObjectを使うには、Newtonsoft.Jsonをプロジェクトに追加する必要があります。

Visual Studioを使っている場合は、NuGetパッケージマネージャーからインストールできます。

ツール
→ NuGet パッケージ マネージャー
→ ソリューションの NuGet パッケージの管理
→ Newtonsoft.Json を検索
→ インストール

.NET CLIを使う場合は、次のコマンドでインストールできます。

Bash
dotnet add package Newtonsoft.Json

パッケージマネージャーコンソールを使う場合は、次のコマンドです。

PowerShell
Install-Package Newtonsoft.Json

インストールが完了すると、C#コード内でJObjectを使えるようになります。

2-2. using Newtonsoft.Json.Linq; の追加

JObjectを使うファイルの先頭には、次のusingを追加します。

C#
using Newtonsoft.Json.Linq;

JSON文字列とC#オブジェクトの変換でJsonConvertも使う場合は、次のusingも追加します。

C#
using Newtonsoft.Json;

基本的に、JObjectJArrayJTokenJValueを使う場合はNewtonsoft.Json.Linqを追加すると覚えておきましょう。

2-3. サンプルで使うJSONデータ

この記事では、次のJSONをサンプルとして使います。

JSON
{
"id": 1,
"name": "Taro Yamada",
"age": 30,
"email": "taro@example.com",
"address": {
"prefecture": "Tokyo",
"city": "Shibuya"
},
"skills": ["C#", "SQL", "Azure"],
"isActive": true
}

このJSONには、文字列、数値、真偽値、オブジェクト、配列が含まれています。JObjectの基本操作を学ぶにはちょうどよい構造です。

2-4. JObjectを扱う前に知っておきたいJSONの基本構造

JSONには主に次のデータ構造があります。

JSONオブジェクトは、波かっこ{}で囲まれ、キーと値の組み合わせで表されます。

JSON
{
"name": "Taro",
"age": 30
}

JSON配列は、角かっこ[]で囲まれ、複数の値を並べます。

JSON
["C#", "SQL", "Azure"]

値には、文字列、数値、真偽値、null、オブジェクト、配列を指定できます。

JSON
{
"text": "hello",
"number": 123,
"flag": true,
"empty": null,
"object": { "key": "value" },
"array": [1, 2, 3]
}

JObjectは、このうち{}で表されるJSONオブジェクトを扱うためのクラスです。配列を扱う場合はJArray、単一の値を扱う場合はJValueを使います。

3. JObjectでJSON文字列を解析する方法

3-1. JObject.ParseでJSON文字列を読み込む

JSON文字列をJObjectに変換するには、JObject.Parseを使います。

C#
using Newtonsoft.Json.Linq;

string json = @"{
""id"": 1,
""name"": ""Taro Yamada"",
""age"": 30
}";

JObject obj = JObject.Parse(json);

Console.WriteLine(obj["name"]);

実行結果は次のとおりです。

Taro Yamada

C#の文字列内でダブルクォーテーションを使う場合は、""のようにエスケープします。上記のように@を付けた逐語的文字列リテラルを使うと、複数行のJSONを書きやすくなります。

通常の文字列として書く場合は、次のようになります。

C#
string json = "{ \"id\": 1, \"name\": \"Taro Yamada\", \"age\": 30 }";
JObject obj = JObject.Parse(json);

3-2. Parse時によくあるエラーと原因

JObject.Parseでよくあるエラーは、JSONの形式が正しくないケースです。

たとえば、次のJSONは不正です。

JSON
{
"name": "Taro",
"age": 30,
}

最後のageの後ろに余分なカンマがあります。このようなJSONをJObject.Parseすると例外が発生します。

C#
try
{
JObject obj = JObject.Parse(json);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

よくある原因は次のとおりです。

  • カンマが多い、または足りない

  • ダブルクォーテーションが閉じていない

  • 波かっこ{}や角かっこ[]の対応が崩れている

  • オブジェクトではなく配列をJObject.Parseしようとしている

  • 空文字列をParseしようとしている

特に注意したいのは、JSON全体が配列の場合です。

JSON
[
{ "id": 1, "name": "Taro" },
{ "id": 2, "name": "Hanako" }
]

この場合はJObject.Parseではなく、JArray.Parseを使います。

C#
JArray array = JArray.Parse(json);

3-3. 不正なJSONを扱うときの注意点

外部APIやユーザー入力から受け取るJSONは、常に正しいとは限りません。そのため、JObject.Parseを使うときは例外処理を入れておくと安全です。

C#
try
{
JObject obj = JObject.Parse(json);
Console.WriteLine(obj["name"]);
}
catch (Newtonsoft.Json.JsonReaderException ex)
{
Console.WriteLine("JSONの形式が正しくありません。");
Console.WriteLine(ex.Message);
}

JsonReaderExceptionをcatchすれば、JSONの読み取りエラーを個別に処理できます。

また、JSONかどうか分からない文字列を処理する場合は、空文字やnullも事前に確認しておきましょう。

C#
if (string.IsNullOrWhiteSpace(json))
{
Console.WriteLine("JSON文字列が空です。");
return;
}

try
{
JObject obj = JObject.Parse(json);
}
catch (Newtonsoft.Json.JsonReaderException)
{
Console.WriteLine("JSONの解析に失敗しました。");
}

3-4. ファイルからJSONを読み込んでJObjectに変換する方法

JSONファイルを読み込む場合は、File.ReadAllTextで文字列として読み込んでからJObject.Parseします。

C#
using System.IO;
using Newtonsoft.Json.Linq;

string path = "settings.json";

string json = File.ReadAllText(path);
JObject obj = JObject.Parse(json);

Console.WriteLine(obj["appName"]);

たとえば、settings.jsonが次の内容だとします。

JSON
{
"appName": "SampleApp",
"version": "1.0.0",
"debug": true
}

この場合、obj["appName"]SampleAppを取得できます。

非同期で読み込む場合は、次のように書けます。

C#
string json = await File.ReadAllTextAsync("settings.json");
JObject obj = JObject.Parse(json);

ASP.NET Coreやデスクトップアプリで設定ファイルを独自に読み込みたい場合にも、この方法はよく使われます。

4. JObjectから値を取得する方法

4-1. プロパティ名を指定して値を取得する

JObjectから値を取得する最も基本的な方法は、プロパティ名を指定する方法です。

C#
JObject obj = JObject.Parse(json);

JToken nameToken = obj["name"];

Console.WriteLine(nameToken);

obj["name"]の戻り値はJTokenです。そのままConsole.WriteLineすると文字列として表示されます。

C#
Console.WriteLine(obj["name"]);
Console.WriteLine(obj["age"]);
Console.WriteLine(obj["email"]);

ただし、この方法で取得した値は厳密にはstringintではなくJTokenです。C#の型として使いたい場合は、次のように変換します。

4-2. ToString・Value<T>で型を指定して取得する

文字列として取得したい場合は、ToString()を使います。

C#
string name = obj["name"]?.ToString();

Console.WriteLine(name);

数値やboolとして取得したい場合は、Value<T>()を使うと便利です。

C#
int age = obj["age"]!.Value<int>();
bool isActive = obj["isActive"]!.Value<bool>();

Console.WriteLine(age);
Console.WriteLine(isActive);

nullの可能性がある場合は、次のように書くと安全です。

C#
int? age = obj["age"]?.Value<int>();
bool? isActive = obj["isActive"]?.Value<bool>();

文字列の場合も、キーが存在しない可能性があるなら?を付けましょう。

C#
string? email = obj["email"]?.Value<string>();

初心者のうちは、次のように使い分けると分かりやすいです。

C#
string? name = obj["name"]?.ToString();
int? age = obj["age"]?.Value<int>();
bool? active = obj["isActive"]?.Value<bool>();

4-3. ネストされたJSONの値を取得する

JSONの中にオブジェクトが入っている場合は、段階的にアクセスします。

JSON
{
"address": {
"prefecture": "Tokyo",
"city": "Shibuya"
}
}

このJSONからcityを取得するには、次のように書きます。

C#
string? city = obj["address"]?["city"]?.ToString();

Console.WriteLine(city);

または、いったんJObjectとして取り出してからアクセスすることもできます。

C#
JObject? address = obj["address"] as JObject;

string? prefecture = address?["prefecture"]?.ToString();
string? city = address?["city"]?.ToString();

Console.WriteLine(prefecture);
Console.WriteLine(city);

ネストが深くなるほど、nullチェックが重要になります。

C#
string? city = obj["user"]?["profile"]?["address"]?["city"]?.ToString();

このように?を付けることで、途中のプロパティが存在しない場合でも例外を防げます。

4-4. 配列データをJArrayとして取得する

JSON配列を取得する場合は、JArrayとして扱います。

JSON
{
"skills": ["C#", "SQL", "Azure"]
}

C#では次のように取得できます。

C#
JArray? skills = obj["skills"] as JArray;

if (skills != null)
{
foreach (JToken skill in skills)
{
Console.WriteLine(skill.ToString());
}
}

JArray内の値を文字列のリストに変換したい場合は、次のように書けます。

C#
List<string> skillList = obj["skills"]?
.Select(x => x.ToString())
.ToList() ?? new List<string>();

配列の先頭要素だけ取得する場合は、インデックスを指定します。

C#
string? firstSkill = obj["skills"]?[0]?.ToString();

Console.WriteLine(firstSkill);

4-5. SelectTokenでパス指定して値を取得する

SelectTokenを使うと、パスを文字列で指定して値を取得できます。

C#
string? city = obj.SelectToken("address.city")?.ToString();

Console.WriteLine(city);

配列の要素も指定できます。

C#
string? firstSkill = obj.SelectToken("skills[0]")?.ToString();

Console.WriteLine(firstSkill);

ネストが深いJSONでは、obj["a"]?["b"]?["c"]のように書くより、SelectToken("a.b.c")の方が読みやすい場合があります。

C#
string? value = obj.SelectToken("user.profile.address.city")?.ToString();

条件付きで配列から値を探す場合にも便利です。

C#
JToken? user = obj.SelectToken("users[?(@.id == 2)]");

ただし、パスの書き方を間違えると値は取得できません。取得できない可能性を考えて、必ずnullチェックを入れるのがおすすめです。

4-6. 存在しないキーやnullを安全に扱う方法

JObjectで最も多いトラブルの一つが、存在しないキーにアクセスしてしまうことです。

C#
string name = obj["name"].ToString();

この書き方は、nameが存在する場合は問題ありません。しかし、nameが存在しない場合はNullReferenceExceptionが発生する可能性があります。

安全に書くなら、次のようにします。

C#
string? name = obj["name"]?.ToString();

デフォルト値を入れたい場合は、null合体演算子??を使います。

C#
string name = obj["name"]?.ToString() ?? "未設定";

数値の場合は、nullable型で受けると安全です。

C#
int? age = obj["age"]?.Value<int>();

必須項目として扱いたい場合は、明示的にチェックしましょう。

C#
JToken? nameToken = obj["name"];

if (nameToken == null || nameToken.Type == JTokenType.Null)
{
throw new Exception("nameは必須です。");
}

string name = nameToken.ToString();

ContainsKeyを使う方法もあります。

C#
if (obj.ContainsKey("email"))
{
string? email = obj["email"]?.ToString();
}

5. JObjectに値を追加・更新・削除する方法

5-1. 新しいプロパティを追加する

JObjectに新しいプロパティを追加するには、キーを指定して値を代入します。

C#
JObject obj = JObject.Parse(@"{
""name"": ""Taro"",
""age"": 30
}");

obj["email"] = "taro@example.com";

Console.WriteLine(obj.ToString());

結果は次のようになります。

JSON
{
"name": "Taro",
"age": 30,
"email": "taro@example.com"
}

数値やboolも同じように追加できます。

C#
obj["isActive"] = true;
obj["score"] = 95;

5-2. 既存プロパティの値を更新する

既存プロパティを更新する場合も、同じように代入します。

C#
obj["name"] = "Hanako";
obj["age"] = 25;

キーが存在しない場合は新規追加、存在する場合は更新になります。

C#
obj["status"] = "active";

更新前に存在確認したい場合は、ContainsKeyを使います。

C#
if (obj.ContainsKey("age"))
{
obj["age"] = 31;
}

5-3. ネストされたオブジェクトに値を追加する

ネストされたJSONオブジェクトに値を追加する場合は、対象のオブジェクトをJObjectとして取得します。

C#
JObject obj = JObject.Parse(@"{
""name"": ""Taro"",
""address"": {
""prefecture"": ""Tokyo""
}
}");

JObject? address = obj["address"] as JObject;

if (address != null)
{
address["city"] = "Shibuya";
}

結果は次のようになります。

JSON
{
"name": "Taro",
"address": {
"prefecture": "Tokyo",
"city": "Shibuya"
}
}

もしaddress自体が存在しない場合は、新しく作成できます。

C#
if (obj["address"] == null)
{
obj["address"] = new JObject();
}

obj["address"]!["city"] = "Shibuya";

5-4. 配列に要素を追加する

配列に要素を追加する場合は、JArrayを使います。

C#
JObject obj = JObject.Parse(@"{
""skills"": [""C#"", ""SQL""]
}");

JArray? skills = obj["skills"] as JArray;

if (skills != null)
{
skills.Add("Azure");
}

結果は次のようになります。

JSON
{
"skills": [
"C#",
"SQL",
"Azure"
]
}

配列が存在しない場合は、新しく作成できます。

C#
if (obj["skills"] == null)
{
obj["skills"] = new JArray();
}

((JArray)obj["skills"]!).Add("Azure");

オブジェクトを配列に追加することもできます。

C#
JArray users = new JArray();

users.Add(new JObject
{
["id"] = 1,
["name"] = "Taro"
});

users.Add(new JObject
{
["id"] = 2,
["name"] = "Hanako"
});

obj["users"] = users;

5-5. プロパティを削除する

プロパティを削除するには、Removeを使います。

C#
obj.Remove("email");

ネストされたオブジェクトのプロパティも削除できます。

C#
JObject? address = obj["address"] as JObject;

address?.Remove("city");

JTokenを取得してから削除する方法もあります。

C#
JToken? token = obj["age"];
token?.Remove();

配列の要素を削除する場合は、JArrayに対してRemoveAtRemoveを使います。

C#
JArray? skills = obj["skills"] as JArray;

skills?.RemoveAt(0);

5-6. 変更後のJObjectをJSON文字列に変換する

変更したJObjectをJSON文字列に戻すには、ToString()を使います。

C#
string updatedJson = obj.ToString();

Console.WriteLine(updatedJson);

通常のToString()では整形されたJSON文字列になります。

JSON
{
"name": "Taro",
"age": 30
}

改行やインデントを省略したコンパクトなJSONにしたい場合は、Formatting.Noneを指定します。

C#
using Newtonsoft.Json;

string compactJson = obj.ToString(Formatting.None);

Console.WriteLine(compactJson);

結果は次のようになります。

JSON
{"name":"Taro","age":30}

APIに送信するJSONやログ出力用のJSONでは、用途に応じて整形あり・なしを使い分けるとよいでしょう。

6. JObjectとC#クラスを相互変換する方法

6-1. C#オブジェクトをJObjectに変換する

C#オブジェクトをJObjectに変換するには、JObject.FromObjectを使います。Newtonsoft.Jsonの公式ドキュメントでも、FromObjectはオブジェクトからJObjectを作成するメソッドとして説明されています。Newtonsoft

C#
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
C#
User user = new User
{
Id = 1,
Name = "Taro",
Age = 30
};

JObject obj = JObject.FromObject(user);

Console.WriteLine(obj.ToString());

出力は次のようになります。

JSON
{
"Id": 1,
"Name": "Taro",
"Age": 30
}

C#オブジェクトをいったんJObjectに変換すると、一部のプロパティを動的に追加・変更しやすくなります。

C#
obj["Role"] = "Admin";
obj["IsActive"] = true;

6-2. JObjectをC#クラスに変換する

JObjectをC#クラスに変換するには、ToObject<T>()を使います。

C#
JObject obj = JObject.Parse(@"{
""Id"": 1,
""Name"": ""Taro"",
""Age"": 30
}");

User? user = obj.ToObject<User>();

Console.WriteLine(user?.Name);

プロパティ名が一致していれば、自動的に値が設定されます。

JSONのプロパティ名がキャメルケースの場合でも、多くのケースではマッピングできます。

JSON
{
"id": 1,
"name": "Taro",
"age": 30
}
C#
User? user = obj.ToObject<User>();

ただし、複雑な変換ルールが必要な場合は、JsonProperty属性などを使って明示的にマッピングするのがおすすめです。

C#
public class User
{
[JsonProperty("id")]
public int Id { get; set; }

[JsonProperty("name")]
public string? Name { get; set; }
}

6-3. JsonConvert.DeserializeObjectとの使い分け

JObjectを使わず、最初からC#クラスに変換したい場合は、JsonConvert.DeserializeObject<T>()を使います。

C#
User? user = JsonConvert.DeserializeObject<User>(json);

一方、JObject.ParseはJSONをまず動的に扱いたい場合に向いています。

C#
JObject obj = JObject.Parse(json);
string? name = obj["name"]?.ToString();

使い分けの目安は次のとおりです。

JsonConvert.DeserializeObject<T>()
→ JSONの構造が決まっている
→ C#クラスとして安全に扱いたい
→ 入力データの型が明確

JObject
→ JSONの構造が固定されていない
→ 一部の値だけ取得したい
→ 途中でJSONを追加・更新・削除したい
→ デバッグや検証で柔軟に扱いたい

実務では、APIレスポンス全体はクラスに変換し、未知の追加項目や一部の動的データだけJObjectで扱うケースもあります。

6-4. 匿名型・dynamicで扱う場合の注意点

Newtonsoft.Jsonでは、JSONをdynamicとして扱うこともできます。

C#
dynamic data = JObject.Parse(json);

Console.WriteLine(data.name);
Console.WriteLine(data.age);

この書き方は短くて便利ですが、存在しないプロパティにアクセスしたときの問題に気づきにくいというデメリットがあります。

C#
Console.WriteLine(data.notExists);

また、型チェックがコンパイル時に効きにくくなるため、大規模なコードでは保守性が下がる場合があります。

匿名型を使う方法もあります。

C#
var template = new
{
name = "",
age = 0
};

var user = JsonConvert.DeserializeAnonymousType(json, template);

Console.WriteLine(user?.name);

ただし、匿名型は一時的な利用に向いています。長く使うデータ構造であれば、専用のC#クラスを作った方が安全です。

7. JObjectを使った実践的なJSON操作サンプル

7-1. APIレスポンスから必要な値を取得する

APIレスポンスから特定の値だけ取得する例です。

JSON
{
"status": "success",
"data": {
"user": {
"id": 100,
"name": "Taro",
"email": "taro@example.com"
}
}
}

C#では次のように取得できます。

C#
JObject response = JObject.Parse(json);

string? status = response["status"]?.ToString();
int? userId = response.SelectToken("data.user.id")?.Value<int>();
string? userName = response.SelectToken("data.user.name")?.ToString();

Console.WriteLine(status);
Console.WriteLine(userId);
Console.WriteLine(userName);

SelectTokenを使うと、ネストされたAPIレスポンスでも必要な値を簡単に取り出せます。

APIレスポンスでは、エラー時に構造が変わることがあります。そのため、値が存在しない前提で安全に書くことが重要です。

C#
JToken? errorMessage = response.SelectToken("error.message");

if (errorMessage != null)
{
Console.WriteLine(errorMessage.ToString());
}

7-2. 設定ファイルの値を読み込む

設定ファイルから値を読み込む例です。

JSON
{
"database": {
"host": "localhost",
"port": 5432,
"name": "sampledb"
},
"logging": {
"level": "Debug"
}
}
C#
string json = File.ReadAllText("settings.json");
JObject settings = JObject.Parse(json);

string? host = settings.SelectToken("database.host")?.ToString();
int port = settings.SelectToken("database.port")?.Value<int>() ?? 5432;
string logLevel = settings.SelectToken("logging.level")?.ToString() ?? "Information";

Console.WriteLine(host);
Console.WriteLine(port);
Console.WriteLine(logLevel);

設定値は存在しない可能性があるため、デフォルト値を用意しておくと安全です。

C#
int timeout = settings.SelectToken("api.timeout")?.Value<int>() ?? 30;

7-3. JSONの一部を書き換える

既存のJSONの一部を書き換える例です。

JSON
{
"name": "Taro",
"age": 30,
"status": "inactive"
}
C#
JObject obj = JObject.Parse(json);

obj["status"] = "active";
obj["updatedAt"] = DateTime.UtcNow.ToString("o");

string updatedJson = obj.ToString();

Console.WriteLine(updatedJson);

ネストされた値を書き換える場合は、次のようにします。

C#
JToken? city = obj.SelectToken("address.city");

if (city != null)
{
city.Replace("Shinjuku");
}

または、親のJObjectを取得して代入します。

C#
JObject? address = obj["address"] as JObject;

if (address != null)
{
address["city"] = "Shinjuku";
}

7-4. 条件に一致する配列要素を検索する

配列内のオブジェクトから、条件に一致する要素を探す例です。

JSON
{
"users": [
{ "id": 1, "name": "Taro", "role": "admin" },
{ "id": 2, "name": "Hanako", "role": "user" },
{ "id": 3, "name": "Jiro", "role": "user" }
]
}

LINQを使うと、次のように検索できます。

C#
JObject obj = JObject.Parse(json);

JArray? users = obj["users"] as JArray;

JToken? targetUser = users?
.FirstOrDefault(x => x["id"]?.Value<int>() == 2);

Console.WriteLine(targetUser?["name"]);

roleuserの要素だけ取り出す場合は、次のようにします。

C#
var normalUsers = users?
.Where(x => x["role"]?.ToString() == "user");

if (normalUsers != null)
{
foreach (JToken user in normalUsers)
{
Console.WriteLine(user["name"]);
}
}

条件に一致する要素を書き換えることもできます。

C#
JToken? user = users?
.FirstOrDefault(x => x["id"]?.Value<int>() == 2);

if (user != null)
{
user["role"] = "admin";
}

7-5. 複数のJSONデータを結合・加工する

複数のJSONを結合して、新しいJSONを作る例です。

C#
JObject user = JObject.Parse(@"{
""id"": 1,
""name"": ""Taro""
}");

JObject profile = JObject.Parse(@"{
""age"": 30,
""city"": ""Tokyo""
}");

新しいJObjectを作成してまとめます。

C#
JObject result = new JObject
{
["userId"] = user["id"],
["name"] = user["name"],
["age"] = profile["age"],
["city"] = profile["city"]
};

Console.WriteLine(result.ToString());

出力は次のようになります。

JSON
{
"userId": 1,
"name": "Taro",
"age": 30,
"city": "Tokyo"
}

配列同士を結合する場合は、JArrayを使います。

C#
JArray array1 = JArray.Parse(@"[
{ ""id"": 1, ""name"": ""Taro"" }
]");

JArray array2 = JArray.Parse(@"[
{ ""id"": 2, ""name"": ""Hanako"" }
]");

JArray merged = new JArray(array1.Concat(array2));

Console.WriteLine(merged.ToString());

複数のAPIレスポンスを1つのJSONに整形したい場合などに便利です。

8. JObjectでよくあるエラーと対処法

8-1. Cannot access child value on Newtonsoft.Json.Linq.JValueの原因

Cannot access child value on Newtonsoft.Json.Linq.JValueは、JValueに対してさらに子要素へアクセスしようとしたときに発生するエラーです。

たとえば、次のJSONがあります。

JSON
{
"name": "Taro"
}

このとき、次のように書くと問題になります。

C#
var value = obj["name"]?["first"];

nameは文字列なので、JValueです。オブジェクトではないため、["first"]のように子要素へアクセスできません。

正しくは、次のように値として取得します。

C#
string? name = obj["name"]?.ToString();

オブジェクトとしてアクセスしたい場合は、対象がJObjectかどうか確認しましょう。

C#
JToken? token = obj["name"];

if (token is JObject nameObject)
{
Console.WriteLine(nameObject["first"]);
}
else
{
Console.WriteLine(token?.ToString());
}

8-2. NullReferenceExceptionを防ぐ書き方

NullReferenceExceptionは、存在しないキーにアクセスした後にToString()などを呼び出すと発生しやすいです。

危険な書き方は次のとおりです。

C#
string name = obj["name"].ToString();

安全な書き方は次のとおりです。

C#
string? name = obj["name"]?.ToString();

デフォルト値を使うなら、次のようにします。

C#
string name = obj["name"]?.ToString() ?? "未設定";

SelectTokenでも同じです。

C#
string city = obj.SelectToken("address.city")?.ToString() ?? "未設定";

JSONは外部から来るデータであることが多いため、「キーが必ず存在する」と決めつけないことが大切です。

8-3. 型変換エラーが起きるケース

Value<int>()などで型を指定して取得するとき、値が変換できない形式だとエラーになります。

JSON
{
"age": "abc"
}

このJSONに対して次のコードを書くと、数値に変換できずエラーになる可能性があります。

C#
int age = obj["age"]!.Value<int>();

安全に処理したい場合は、文字列として取得してからint.TryParseを使います。

C#
string? ageText = obj["age"]?.ToString();

if (int.TryParse(ageText, out int age))
{
Console.WriteLine(age);
}
else
{
Console.WriteLine("ageは数値ではありません。");
}

boolやDateTimeも同様に、外部データでは変換できない可能性を考慮しましょう。

C#
string? dateText = obj["createdAt"]?.ToString();

if (DateTime.TryParse(dateText, out DateTime createdAt))
{
Console.WriteLine(createdAt);
}

8-4. SelectTokenで値が取得できない原因

SelectTokenで値が取得できない場合、主な原因はパスの指定ミスです。

たとえば、次のJSONがあります。

JSON
{
"user": {
"profile": {
"name": "Taro"
}
}
}

正しいパスは次のとおりです。

C#
string? name = obj.SelectToken("user.profile.name")?.ToString();

間違ったパスを指定すると、結果はnullになります。

C#
string? name = obj.SelectToken("users.profile.name")?.ToString();

また、配列の指定ミスにも注意が必要です。

JSON
{
"users": [
{ "id": 1, "name": "Taro" }
]
}

配列の先頭要素を取得する場合は、次のように書きます。

C#
string? name = obj.SelectToken("users[0].name")?.ToString();

users.nameでは取得できません。配列の場合は[0]のようにインデックスを指定する必要があります。

8-5. 配列とオブジェクトを間違えたときの対処法

JSONでよくある間違いが、配列とオブジェクトの混同です。

オブジェクトは{}です。

JSON
{
"id": 1,
"name": "Taro"
}

配列は[]です。

JSON
[
{ "id": 1, "name": "Taro" },
{ "id": 2, "name": "Hanako" }
]

JSON全体がオブジェクトならJObject.Parseを使います。

C#
JObject obj = JObject.Parse(json);

JSON全体が配列ならJArray.Parseを使います。

C#
JArray array = JArray.Parse(json);

どちらか分からない場合は、JToken.Parseを使って判定できます。

C#
JToken token = JToken.Parse(json);

if (token is JObject obj)
{
Console.WriteLine("JSONオブジェクトです。");
}
else if (token is JArray array)
{
Console.WriteLine("JSON配列です。");
}

APIによっては、正常時はオブジェクト、検索結果一覧では配列、エラー時は別構造のオブジェクトを返すことがあります。最初にJSONの形を確認する習慣をつけましょう。

9. JObjectを使うときのベストプラクティス

9-1. nullチェックを前提に安全に書く

JObjectを使うときは、常にnullの可能性を考えて書くことが重要です。

避けたい書き方は次のとおりです。

C#
string name = obj["name"].ToString();

おすすめの書き方は次のとおりです。

C#
string name = obj["name"]?.ToString() ?? "未設定";

ネストされた値も同じです。

C#
string city = obj.SelectToken("address.city")?.ToString() ?? "未設定";

必須項目の場合は、nullなら例外を出すなど、明確に処理しましょう。

C#
string name = obj["name"]?.ToString()
?? throw new InvalidOperationException("nameは必須です。");

安全なコードにすることで、APIレスポンスの変更や想定外のデータにも強くなります。

9-2. 型が決まっているJSONはクラス化する

JObjectは便利ですが、すべてをJObjectで扱うのが最適とは限りません。JSONの構造が決まっている場合は、C#クラスに変換した方が安全です。

C#
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
C#
User? user = JsonConvert.DeserializeObject<User>(json);

クラス化するメリットは次のとおりです。

  • 型が明確になる

  • 入力補完が効く

  • コンパイル時にミスを見つけやすい

  • 保守性が高くなる

  • チーム開発で意図が伝わりやすい

一方で、構造が頻繁に変わるJSONや、一部だけ読みたいJSONではJObjectが便利です。

9-3. 複雑なJSONはSelectTokenを活用する

ネストが深いJSONでは、SelectTokenを使うとコードが読みやすくなります。

C#
string? value = obj["a"]?["b"]?["c"]?["d"]?.ToString();

このようなコードは、階層が深くなるほど読みにくくなります。

SelectTokenを使うと、次のように書けます。

C#
string? value = obj.SelectToken("a.b.c.d")?.ToString();

配列も含めて指定できます。

C#
string? name = obj.SelectToken("users[0].profile.name")?.ToString();

ただし、文字列のパスはコンパイル時にチェックされません。パスのタイプミスに注意し、取得結果がnullになる可能性を前提にしましょう。

9-4. 可読性を保つためのコード整理

JObjectを多用すると、コードが読みにくくなることがあります。

たとえば、次のようなコードは意図が分かりにくくなりがちです。

C#
string? value = obj["data"]?["user"]?["profile"]?["address"]?["city"]?.ToString();

このような場合は、途中の値を変数に分けると読みやすくなります。

C#
JToken? profile = obj.SelectToken("data.user.profile");

string? city = profile?["address"]?["city"]?.ToString();
string? zipCode = profile?["address"]?["zipCode"]?.ToString();

また、よく使う取得処理はメソッド化すると便利です。

C#
static string GetString(JObject obj, string path, string defaultValue = "")
{
return obj.SelectToken(path)?.ToString() ?? defaultValue;
}

使用例は次のとおりです。

C#
string name = GetString(obj, "user.name", "未設定");
string city = GetString(obj, "user.address.city", "未設定");

可読性を保つには、JObjectの操作を1か所にまとめ、アプリケーション全体に散らばらせないことも大切です。

9-5. パフォーマンス面で注意すべきポイント

JObjectはJSONをメモリ上のオブジェクト構造として扱うため、柔軟ですが、大きなJSONを大量に処理する場合はメモリ使用量に注意が必要です。

注意したいケースは次のとおりです。

  • 数十MB以上の大きなJSONを読み込む

  • 大量のJSONをループで何度もParseする

  • 同じJSONに対して何度もSelectTokenを呼び出す

  • 不要なToString()を繰り返す

  • JSON全体を扱う必要がないのに丸ごとJObject化する

パフォーマンスを意識するなら、次のような工夫が有効です。

C#
JToken? userToken = obj.SelectToken("data.user");

string? name = userToken?["name"]?.ToString();
string? email = userToken?["email"]?.ToString();

同じパスの親要素を何度も検索するのではなく、いったん変数に入れて使い回すと読みやすく、無駄も減らせます。

構造が固定されている大規模データでは、C#クラスへのデシリアライズやSystem.Text.Jsonの利用も検討するとよいでしょう。

10. C# JObjectに関するよくある質問

10-1. JObjectとJsonConvertの違いは?

JObjectはJSONオブジェクトをメモリ上で操作するためのクラスです。JSONの値を取得したり、追加・更新・削除したりできます。

一方、JsonConvertはJSON文字列とC#オブジェクトを変換するためによく使われるクラスです。

C#
JObject obj = JObject.Parse(json);

これは、JSONを動的に扱う方法です。

C#
User? user = JsonConvert.DeserializeObject<User>(json);

これは、JSONをC#クラスに変換する方法です。

簡単に言うと、JObjectはJSONをそのまま操作したいとき、JsonConvertはC#クラスに変換したいときに使います。

10-2. JObjectとdynamicはどちらを使うべき?

基本的には、明確にJSONを操作したいならJObjectを使うのがおすすめです。

C#
string? name = obj["name"]?.ToString();

dynamicは短く書けます。

C#
dynamic data = JObject.Parse(json);
Console.WriteLine(data.name);

しかし、dynamicはプロパティ名のミスに気づきにくく、保守性が下がる場合があります。小さな検証コードや一時的な処理では便利ですが、業務アプリケーションではJObjectまたはC#クラスを使う方が安全です。

10-3. JObjectで配列を扱うには?

JObject内の配列は、JArrayとして取得します。

C#
JArray? items = obj["items"] as JArray;

if (items != null)
{
foreach (JToken item in items)
{
Console.WriteLine(item);
}
}

配列の中がオブジェクトの場合は、次のように値を取得できます。

C#
foreach (JToken item in items)
{
string? name = item["name"]?.ToString();
Console.WriteLine(name);
}

JSON全体が配列の場合は、JObject.ParseではなくJArray.Parseを使います。

C#
JArray array = JArray.Parse(json);

10-4. JObjectの値がnullかどうか判定するには?

キーが存在しない場合と、JSON上で明示的にnullが入っている場合があります。

JSON
{
"name": null
}

安全に判定するには、次のように書きます。

C#
JToken? token = obj["name"];

if (token == null || token.Type == JTokenType.Null)
{
Console.WriteLine("nameはnullです。");
}

単純に文字列として取得し、nullならデフォルト値を使う場合は次の書き方で十分です。

C#
string name = obj["name"]?.ToString() ?? "未設定";

ただし、JTokenType.Nullの値に対してToString()すると空文字のように見えることがあるため、厳密に判定したい場合はTypeを確認しましょう。

10-5. JObjectは現在も使って問題ない?

JObjectは現在でも多くのC#プロジェクトで使われています。特に、既存システムでNewtonsoft.Jsonを使っている場合や、複雑で柔軟なJSON操作が必要な場合には便利です。

一方で、新しい.NETプロジェクトではSystem.Text.Jsonを標準的に使うケースも増えています。System.Text.Jsonは.NETの標準機能として提供され、JsonDocumentJsonNodeなどのDOM操作も利用できます。Microsoft Learn

そのため、使い分けとしては次の考え方がおすすめです。

JObjectを使うとよいケース
→ 既存プロジェクトでNewtonsoft.Jsonを使っている
→ JSONを柔軟に追加・更新・削除したい
→ SelectTokenを使って複雑なJSONを扱いたい
→ JSON構造が固定されていない

System.Text.Jsonを検討するとよいケース
→ 新規プロジェクトで標準ライブラリを使いたい
→ パフォーマンスを重視したい
→ 型が決まったJSONをシリアライズ・デシリアライズしたい

つまり、JObjectは今でも十分使える選択肢です。ただし、プロジェクトの方針や.NETのバージョン、パフォーマンス要件に応じて選ぶことが大切です。

まとめ

C#のJObjectは、Newtonsoft.JsonでJSONオブジェクトを柔軟に扱うための便利なクラスです。JSON文字列をJObject.Parseで読み込み、プロパティ名やSelectTokenを使って値を取得できます。また、値の追加・更新・削除、配列の操作、C#クラスとの相互変換も簡単に行えます。

特に、APIレスポンスや設定ファイルのように、JSONの一部だけを取り出したい場面ではJObjectが非常に役立ちます。ネストされたJSONにはSelectToken、配列にはJArray、単一の値にはJValueというように、JSONの構造に応じて使い分けることがポイントです。

一方で、JObjectは柔軟な反面、存在しないキーや型変換ミスによるエラーが起きやすい面もあります。?.によるnullチェック、Value<T>()の適切な利用、JTokenTypeの確認などを意識すると、安全で読みやすいコードになります。

JSONの構造が決まっている場合はC#クラスへの変換、構造が動的な場合はJObjectというように使い分けることで、C#でのJSON処理を効率よく実装できます。