1. はじめに
Newtonsoft.Json は良い意味でいうと一種の「恐竜」だ。事実上の標準として長年 .NET エコシステムで JSON を扱ってきて、System.Text.Json が出てくる前から広く使われてきた。何百万ものプロジェクト、何千ものライブラリやフレームワーク(最近まで ASP.NET Core の多くの部分も含む)が Json.NET を使っている。
利点は次の通り:
- 機能が豊富で柔軟:Json.NET はシリアライズ/デシリアライズのプロセスをカスタマイズするための多数の設定、属性、機能を提供する。System.Text.Json が苦労する、あるいはできないこともこなせる。
- 「完璧でない」JSON に寛容:外部システムから来るデータは常に綺麗とは限らない — Json.NET はそうしたケースに比較的寛容で、余計な手間なくデシリアライズできることが多い。
- 後方互換性がある:プロジェクトやライブラリが Json.NET に依存しているなら、それを扱えるスキルは依然として必要だ。
もちろん、System.Text.Json の方が高速で現代的な要件を念頭に作られている。でも、特定のロジックや最大限の柔軟性が必要な場面では Newtonsoft.Json がいまだに強力なツールだよ。
Newtonsoft.Json をどうやって導入する?
これはサードパーティのライブラリなので、NuGet でパッケージを追加してね:
- プロジェクトを開く。
- Solution Explorer でプロジェクトを右クリック。
- “Manage NuGet Packages...” を選択。
- 検索ボックスに Newtonsoft.Json と入力。
- パッケージを選んで「Install」をクリック。
インストール後は依存関係が追加されて、Json.NET を使えるようになるよ!
Newtonsoft.Json と System.Text.Json の機能比較
| 機能 | System.Text.Json | Newtonsoft.Json |
|---|---|---|
| 簡単なシリアライズ/デシリアライズ | はい | はい |
| プロパティ用属性のサポート | 部分的に | 完全 |
| カスタムコンバーター | はい | はい |
| プライベートフィールドの扱い | いいえ | はい |
| 動的な構造の扱い | 制限あり | はい |
| LINQ to JSON (JObject/JArray) | いいえ | はい |
| Reference Loop Handling | はい | はい |
| DataTable, DataSet と複雑な型のサポート | いいえ | はい |
| パフォーマンス | 優れている | 良い |
2. 単純オブジェクトのシリアライズとデシリアライズの例
ここではレクチャーで使う一般的なゲーム構造を取り上げる:
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
public List<string> Inventory { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int X { get; set; }
public int Y { get; set; }
}
では Player オブジェクトを JSON に保存して復元しよう:
using Newtonsoft.Json;
Player player1 = new Player
{
Name = "Aragorn",
Health = 100,
IsAlive = true,
Inventory = new List<string> { "sword", "bow", "healing potion" },
Position = new Position { X = 10, Y = 25 }
};
// JSON 文字列へのシリアライズ
string json = JsonConvert.SerializeObject(player1, Formatting.Indented);
Console.WriteLine(json);
// 逆に Player オブジェクトへデシリアライズ
Player player2 = JsonConvert.DeserializeObject<Player>(json);
Console.WriteLine($"名前: {player2.Name}, 体力: {player2.Health}");
全部?まあ、ほとんどだけど、これは氷山の一角に過ぎない。Newtonsoft.Json が他のライブラリよりどう面白くて柔軟なのか見ていこう。
3. フォーマット、設定、拡張パラメータ
オブジェクトをシリアライズするとき、コンパクトな文字列にも見やすく整形された JSON にもできる。振る舞いはパラメータや設定で制御できるよ。
例: フォーマットのいくつかのパターン
// 見やすい整形された JSON
string prettyJson = JsonConvert.SerializeObject(player1, Formatting.Indented);
// コンパクトな(ミニファイド)JSON
string compactJson = JsonConvert.SerializeObject(player1, Formatting.None);
シリアライズ設定
必要なら JsonSerializerSettings を渡して細かく設定できる:
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore, // null 値のフィールドをスキップ
DefaultValueHandling = DefaultValueHandling.Ignore, // デフォルト値のフィールドをスキップ
Formatting = Formatting.Indented
};
string customJson = JsonConvert.SerializeObject(player1, settings);
出力フォーマットを最大限コントロールできる:空のフィールドを飛ばしたければその通りにできるし、プライベートなプロパティまでシリアライズしたければ契約(contract)を調整すればいい。
4. 属性によるフィールドとプロパティの制御
Newtonsoft.Json はクラス内でシリアライズを調整するための強力な属性をサポートしている。
JsonProperty — プロパティ名の変更
JSON プロトコルが別名を要求する場合:
public class Player
{
[JsonProperty("player_name")]
public string Name { get; set; }
// ...
}
生成される JSON の例:
{ "player_name": "Aragorn", ... }
JsonIgnore — プロパティを無視する
public class Player
{
[JsonIgnore]
public int Health { get; set; }
}
これで Health フィールドはシリアライズ時に消える。
JsonConverter — カスタム変換
特定のフィールドをどのコンバーターで処理するか指定できる。
public class Player
{
[JsonConverter(typeof(InventoryToStringConverter))]
public List<string> Inventory { get; set; }
}
(コンバーターの詳細は下で説明するよ。)
5. ネストされたオブジェクトとコレクションの扱い
Json.NET はネストされたオブジェクトや配列、コレクション、辞書を得意とする。
例: 辞書 (Dictionary)
public class GameStats
{
public Dictionary<string, int> Scores { get; set; }
}
GameStats stats = new GameStats
{
Scores = new Dictionary<string, int>
{
["Alice"] = 1023,
["Bob"] = 999
}
};
string statsJson = JsonConvert.SerializeObject(stats, Formatting.Indented);
Console.WriteLine(statsJson);
生成される JSON は次のようになる:
{
"Scores": {
"Alice": 1023,
"Bob": 999
}
}
6. 複雑な構造: 循環参照、Self-Referencing Objects
時々オブジェクト同士が相互参照していることがある。Newtonsoft.Json は専用の設定を使ってこうした構造のシリアライズをサポートしている。
例: 循環参照の解決
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public List<Person> Children { get; set; }
}
// 循環のためにシリアライズ設定をする
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore // または .Serialize
};
Person p1 = new Person { Name = "パパ" };
Person p2 = new Person { Name = "息子", Parent = p1 };
p1.Children = new List<Person> { p2 };
string json = JsonConvert.SerializeObject(p1, settings);
Console.WriteLine(json);
デフォルトで ReferenceLoopHandling = Error にしておくと例外が投げられる。意図しない無限シリアライズを防ぐための保護だよ。
7. 動的構造の扱い: JObject, JArray
JSON の構造が事前にわからない、あるいは実行時に変わる場合は、C# の強い型付けを使わずに動的に扱う方が楽なことがある。
主な型:
- JObject — JSON オブジェクトの表現。
- JArray — 配列の表現。
using Newtonsoft.Json.Linq;
// 文字列を JObject にパース
string json = @"{ 'name': 'Aragorn', 'health': 100 }";
JObject obj = JObject.Parse(json);
Console.WriteLine((string)obj["name"]); // Aragorn
Console.WriteLine((int)obj["health"]); // 100
// 動的にプロパティを追加
obj["class"] = "Ranger";
Console.WriteLine(obj.ToString());
配列のイテレーション
string jsonArr = @"['apple', 'banana', 'cherry']";
JArray array = JArray.Parse(jsonArr);
foreach (JToken item in array)
{
Console.WriteLine(item);
}
8. バージョニングと必須フィールドのサポート
JSON フォーマットは進化するし、フィールドが欠けることもある。属性や設定を使って対応しよう:
- [JsonProperty(Required = Required.Always)] — フィールドの存在を必須にする(なければ例外)。
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] — フィールドがない場合にデフォルトを入れる。
public class Player
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[DefaultValue(50)]
public int Health { get; set; }
}
9. 日付と時間フォーマットの変換
日付を扱うときは明示的なフォーマットが必要になることが多い。
var dateSettings = new JsonSerializerSettings
{
DateFormatString = "yyyy-MM-dd"
};
string json = JsonConvert.SerializeObject(DateTime.Now, dateSettings);
Console.WriteLine(json); // "2024-06-15"
逆にデシリアライズする場合:
string dateJson = "\"2024-06-15\""; // 注意: これは引用符で囲まれた文字列だよ!
DateTime dt = JsonConvert.DeserializeObject<DateTime>(dateJson);
Console.WriteLine(dt);
GO TO FULL VERSION