CodeGym /课程 /C# SELF /深入了解 Newtonsoft.Json

深入了解 Newtonsoft.Json

C# SELF
第 47 级, 课程 2
可用

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
  }
]

常用设置:

属性 说明
NullValueHandling
如何处理 null 属性(跳过它们还是显式写入 null)
DefaultValueHandling
是否跳过默认值
ReferenceLoopHandling
如何处理循环引用
DateFormatString
日期时间字符串的格式

示例:跳过 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)或调整数据模型。

2
任务
C# SELF, 第 47 级, 课程 2
已锁定
处理嵌套结构
处理嵌套结构
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION