1. 介紹
Newtonsoft.Json 在某種意義上是個「老牌但可靠」的工具。多年前它基本上就是 .NET 生態裡處理 JSON 的事實標準,早在 System.Text.Json 出現之前就廣泛被採用。數百萬個專案、成千上萬的函式庫與 framework(甚至 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 |
|---|---|---|
| 簡單序列化/反序列化 | 是 | 是 |
| 屬性標註支援 | 部分支援 | 完整支援 |
| 自訂 converters | 是 | 是 |
| 對私有欄位的處理 | 否 | 是 |
| 處理動態結構 | 有限支援 | 是 |
| 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);
// 精簡(minified)的 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 支援一套強大的屬性系統,可以直接在 class 層級控制序列化行為。
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 — 自訂轉換邏輯
可以指定某個欄位用哪個 converter 來處理:
public class Player
{
[JsonConverter(typeof(InventoryToStringConverter))]
public List<string> Inventory { get; set; }
}
(關於 converters 的細節在下面會講)
5. 處理巢狀物件與集合
Json.NET 對巢狀物件、陣列、集合、字典處理得很好。
範例:字典
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 結構事先不知道或會動態改變時,可以用不強型別的動態方式處理,不必先寫一堆 POCO 類別。
主要型別:
- 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