CodeGym /课程 /C# SELF /使用 Newtonsoft.Json

使用 Newtonsoft.Json

C# SELF
第 44 级, 课程 4
可用

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 添加包:

  1. 打开项目。
  2. 在 Solution Explorer 中右键点击项目。
  3. 选择 "Manage NuGet Packages..."。
  4. 在搜索框里输入 Newtonsoft.Json。
  5. 选中包并点击 "Install"。

安装完成后会出现新的依赖 —— 现在就可以用 Json.NET 了!

Newtonsoft.Json 和 System.Text.Json 的功能对比

功能 System.Text.Json Newtonsoft.Json
简单的序列化/反序列化 是 是
对属性的 attribute 支持 部分 完整
自定义转换器 是 是
对私有字段的处理 否 是
处理动态结构 有限制 是
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,行为由参数和设置控制。

示例:不同的格式化方式

// 美观可读的 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 — 自定义转换

可以指定某个字段使用哪个 converter 来处理。

public class Player
{
    [JsonConverter(typeof(InventoryToStringConverter))]
    public List<string> Inventory { get; set; }
}

(关于 converter 的细节在下面会讲。)

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. 复杂结构:循环引用,自引用对象

有时对象之间互相引用。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);
1
调查/小测验
标准序列化类第 44 级,课程 4
不可用
标准序列化类
用于序列化的类和库
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION