1. 介紹
如果你剛開始接觸 .NET 的序列化,很自然會問:既然有內建的 System.Text.Json,為什麼還要另一個套件?答案很簡單:Newtonsoft.Json 出現得比較早,經年累月演化成為在 .NET 世界裡仍然非常靈活的 JSON 工具。
它幾乎成了事實上的標準,因為支援許多進階場景:自訂 contract、強大的 converter、序列化 private 欄位、LINQ 到 JSON、動態物件(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 的列表了!
字典序列化與反序列化的特性
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)]。
序列化匿名與動態物件
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 vs System.Text.Json 的比較
| Newtonsoft.Json (Json.NET) | System.Text.Json (.NET) | |
|---|---|---|
| 支援 .NET | .NET Framework/Standard/6+ | .NET Core 3.0+ / .NET 5/6/9 |
| LINQ 到 JSON (JObject/JArray) | 有 | 沒有 |
| 彈性設定 | 非常豐富 | 有限 |
| 屬性 ([JsonProperty], ...) | 有 | 有(部分,功能較少) |
| 支援 private 屬性 | 有 | 沒有 |
| 速度 | 在某些場景較慢 | 較快 |
| 複雜 converter | 有 | 有(但彈性較低,目前如此) |
| 支援 DataTable, DataSet | 有 | 沒有 |
| 文件與範例 | 大量 | 正在成長 |
9. 新手常犯的錯誤與陷阱
錯誤 #1:屬性在反序列化後變成 null。
常見原因是屬性沒有 setter,或是缺少無參構造函式——序列化器沒辦法把值填回物件。
錯誤 #2:JSON 的欄位名稱與類別不一致。
如果在 JSON 裡欄位是 "fullName",但在類別裡是 FullName,可以用 [JsonProperty] 或設定 ContractResolver 來對齊名稱。
錯誤 #3:預設情況下只序列化 public 屬性。
private 欄位/屬性不會被序列化,除非加 converter 或特殊的 resolver/contract。
錯誤 #4:循環參考導致 StackOverflowException。
彼此互相參考的物件會在沒有處理的情況下讓序列化陷入無窮迴圈。要設定參考處理(例如用 ReferenceLoopHandling)或調整資料模型。
GO TO FULL VERSION