1. 介绍
如果你刚开始接触 .NET 里的序列化,很自然会想:既然有内置的 System.Text.Json,为什么还需要另外一个库?答案很简单:Newtonsoft.Json 比较早出现,经过多年发展,依然是 .NET 里最灵活的 JSON 工具之一。
它几乎成了事实上的标准,因为它支持复杂场景:自定义 contract、强大的 converters、序列化私有字段、LINQ to 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], ...) | 有 | 有(部分,功能较少) |
| 支持私有属性 | 有 | 没有 |
| 速度 | 在某些场景下较慢 | 更快 |
| 复杂的 converters | 有 | 有(但不够灵活,目前) |
| 支持 DataTable, DataSet | 有 | 没有 |
| 文档与示例 | 海量 | 在成长中 |
9. 新手常犯的错误和典型坑
错误 №1: 属性在反序列化后变成 null。
通常是因为属性没有 setter,或者没有无参构造 —— 序列化器没法填充对象。
错误 №2: JSON 与类的属性名不匹配。
如果 JSON 里字段是 "fullName",而类里是 FullName,请使用 [JsonProperty] 或者配置 ContractResolver 来做名称映射。
错误 №3: 默认情况下序列化只处理公共属性。
私有字段/属性不会被序列化,除非加额外配置。需要 converters 或者自定义 resolver/contract。
错误 №4: 循环引用导致 StackOverflowException。
对象间互相引用会让序列化无限递归,除非配置引用处理(比如 ReferenceLoopHandling)或调整数据模型。
GO TO FULL VERSION