1. はじめに
.NETでシリアライゼーションを学び始めたばかりなら、組み込みのSystem.Text.Jsonがあるのに、なぜ別のライブラリが必要なのか疑問に思うのは自然です。答えはシンプル:Newtonsoft.Jsonは早くから存在し、長年にわたって今でも最も柔軟なJSONツールの一つに成長したからです。
Json.NETは複雑なシナリオをサポートするため事実上の標準になりました:カスタムコントラクト、高度なコンバータ、privateフィールドのシリアライズ、JSONへのLINQ、動的オブジェクト(JObject)、循環参照の柔軟な処理、多様な日付/時間フォーマットなど。多くのライブラリやAPIが今でも内部でJson.NETを使っています。
重要:いくつかのシナリオでは組み込みのSystem.Text.JsonはまだNewtonsoft.Jsonの機能に劣るため、Json.NETを学ぶ価値は残っています。
Newtonsoft.Json (Json.NET) の導入方法
NuGetでパッケージをインストールしてください:
dotnet add package Newtonsoft.Json
名前空間を追加:
using Newtonsoft.Json;
2. Newtonsoft.Jsonによるシリアライズ
典型的なクラスPersonを使います:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
オブジェクトをJSON文字列にシリアライズ
Person person = new Person { Name = "イワン", Age = 30 };
// JSONへシリアライズ
string json = JsonConvert.SerializeObject(person);
Console.WriteLine(json);
// 出力: {"Name":"イワン","Age":30}
JSONをオブジェクトにデシリアライズ
string json = "{\"Name\":\"イワン\",\"Age\":30}";
Person person = JsonConvert.DeserializeObject<Person>(json);
Console.WriteLine($"{person.Name}, {person.Age}");
// 出力: イワン, 30
「裏側」で何が起きているのか?
Newtonsoft.Jsonは公開プロパティ(public)をすべて走査してそれらをJSONにシリアライズし、文字列に書き出します。デシリアライズ時はJSONのキーをプロパティ名にマップしてオブジェクトを埋めます。
3. オブジェクトコレクションのシリアライズ
コレクションと配列のシリアライズ
コレクションは「箱から出してすぐ」動作します。
List<Person> people = new List<Person>
{
new Person { Name = "イワン", Age = 30 },
new Person { Name = "マリヤ", Age = 25 }
};
string json = JsonConvert.SerializeObject(people);
// 出力: [{"Name":"イワン","Age":30},{"Name":"マリヤ","Age":25}]
List<Person> deserialized = JsonConvert.DeserializeObject<List<Person>>(json);
// これで再びPersonのリストになります!
辞書(Dictionary)のシリアライズとデシリアライズの特徴
var dict = new Dictionary<string, int>
{
["apple"] = 2,
["banana"] = 5
};
string json = JsonConvert.SerializeObject(dict);
// 出力: {"apple":2,"banana":5}
var deserializedDict = JsonConvert.DeserializeObject<Dictionary<string,int>>(json);
// 問題なく動きます!
キーが文字列でない場合(例えばDictionary<int,string>)は、Json.NETはシリアライズ時にキーを文字列に変換し、デシリアライズ時に元の型に戻そうとします。複雑なキー(例:Guid)を使うなら、Dictionary<string, TValue>を使う方が安全です。
4. ネストしたオブジェクトと階層構造の扱い
public class Order
{
public int Id { get; set; }
public Person Customer { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public string Title { get; set; }
public double Price { get; set; }
}
Order order = new Order
{
Id = 123,
Customer = new Person { Name = "イワン", Age = 30 },
Products = new List<Product>
{
new Product { Title = "ノートパソコン", Price = 50000.0 },
new Product { Title = "マウス", Price = 1500.0 }
}
};
string json = JsonConvert.SerializeObject(order);
Console.WriteLine(json);
結果:ネストされたオブジェクトはJSON構造内で正しく表現されます。
5. 属性を使ったシリアライズの調整
プロパティを無視する
public class Person
{
public string Name { get; set; }
[JsonIgnore]
public int Age { get; set; }
}
これでAgeはJSONに含まれません。
プロパティ名を変更する
public class Person
{
[JsonProperty("full_name")]
public string Name { get; set; }
}
JSONでは名前が"full_name"になります。
6. JsonSerializerSettingsによる柔軟な設定
フォーマット(読みやすい複数行JSON):
string json = JsonConvert.SerializeObject(
people,
Formatting.Indented
);
結果:
[
{
"Name": "イワン",
"Age": 30
},
{
"Name": "マリヤ",
"Age": 25
}
]
よく使う設定:
| プロパティ | 説明 |
|---|---|
|
nullプロパティをどう扱うか(省略するか明示的にnullを書くか) |
|
デフォルト値を省略するかどうか |
|
循環参照をどう扱うか |
|
日付/時間の文字列フォーマット |
nullを省略する例:
string json = JsonConvert.SerializeObject(
person,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }
);
7. 動的JSON構造の扱い:JObject、JArrayなど
JSONの構造が事前にわからない場合はNewtonsoft.Json.Linqの型を使います:
using Newtonsoft.Json.Linq;
string json = @"{
'Name': 'イワン',
'Age': 30,
'Skills': ['C#', 'SQL', 'JSON']
}";
JObject obj = JObject.Parse(json);
Console.WriteLine(obj["Name"]); // イワン
Console.WriteLine(obj["Skills"][0]); // C#
その場でJSONを作る例:
var jObj = new JObject
{
["Status"] = "Success",
["Result"] = new JArray("item1", "item2", "item3")
};
Console.WriteLine(jObj.ToString(Formatting.Indented));
JObjectとJArrayはJSONオブジェクトと配列の表現で、便利なコレクションです。
8. 役に立つ注意点
循環参照
Newtonsoft.Jsonは循環参照を柔軟に処理できます:
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
string json = JsonConvert.SerializeObject(obj, settings);
これで循環は無視されます。参照を保持したい場合はReferenceLoopHandling.Serializeと合わせて[JsonObject(IsReference = true)]を使う方法があります。
匿名オブジェクトやdynamicのシリアライズ
var anon = new { Foo = 42, Bar = "Hello" };
string json = JsonConvert.SerializeObject(anon);
// {"Foo":42,"Bar":"Hello"}
バリデーションとエラー処理
try
{
Person p = JsonConvert.DeserializeObject<Person>(brokenJson);
}
catch (JsonSerializationException ex)
{
Console.WriteLine("デシリアライズ時のエラー: " + ex.Message);
}
Newtonsoft.Json と System.Text.Json の比較
| Newtonsoft.Json (Json.NET) | System.Text.Json (.NET) | |
|---|---|---|
| サポートされる.NET | .NET Framework/Standard/6+ | .NET Core 3.0+ / .NET 5/6/9 |
| JSONへのLINQ (JObject/JArray) | はい | いいえ |
| 柔軟な設定 | 非常に豊富 | 制限あり |
| 属性([JsonProperty], ...) | あり | あり(ただし機能は限定的) |
| privateプロパティのサポート | あり | なし |
| 速度 | シナリオによっては遅い | 高速 |
| 複雑なコンバータ | あり | あり(ただし柔軟性は低め) |
| DataTable, DataSetのサポート | あり | なし |
| ドキュメントとサンプル | 豊富 | 増加中 |
9. 初心者のミスとよくある落とし穴
ミス #1: デシリアライズ後にプロパティがnullになる。
多くの場合、プロパティにsetterがないか、パラメータなしコンストラクタがないためにシリアライザがオブジェクトを埋められません。
ミス #2: JSONとクラスのプロパティ名が一致しない。
JSONにフィールドが"fullName"で、クラス側がFullNameなら、[JsonProperty]を使うか、名前のマッピング用にContractResolverを設定してください。
ミス #3: デフォルトでは公開プロパティしかシリアライズされない。
privateフィールド/プロパティは追加設定なしではシリアライズされません。コンバータや専用のリゾルバ/コントラクトが必要です。
ミス #4: 循環参照でStackOverflowExceptionになる。
相互参照するオブジェクトは設定無しでシリアライズすると無限ループになります。参照の扱い(例:ReferenceLoopHandling)を設定するか、データモデルを見直してください。
GO TO FULL VERSION