在现代 C# 开发中,JSON(JavaScript Object Notation)已成为数据交换和配置存储的事实标准。无论是与前端 API 交互、读写配置文件,还是进行服务间通信,熟练处理 JSON 都是一项必备技能。
本文将全面覆盖 C# 中 JSON 处理的核心知识,重点讲解两个主流库的使用:Newtonsoft.Json(Json.NET)和 .NET Core 3.0+ 内置的 System.Text.Json。我将从基础概念讲起,逐步深入到高级场景,并提供实用的最佳实践和问题排查方案。
一、JSON 数据格式详解
1. JSON 的基本结构
JSON 数据格式主要有两种核心结构:
- 对象(Object):一个无序的键 / 值对集合,以
{开始,}结束。每个键后跟:,键 / 值对之间用,分隔。 - 数组(Array):一个有序的值集合,以
[开始,]结束,值之间用,分隔。
2. JSON 的数据类型
JSON 支持以下数据类型:
- 字符串(String):双引号包围的 Unicode 字符序列(如
"name": "张三")。 - 数字(Number):整数、浮点数(正数、负数、零,如
"age": 30)。 - 布尔值(Boolean):
true或false(如"isStudent": false)。 - null:表示空值(如
"score": null)。 - 对象(Object):嵌套的键 / 值对集合(如
"address": { "city": "北京" })。 - 数组(Array):有序的值列表(如
"hobbies": ["篮球", "编程"])。
3. JSON 示例
{
"name": "张三",
"age": 30,
"isStudent": false,
"hobbies": ["篮球", "足球", "阅读"],
"address": {
"street": "123 Main Street",
"city": "北京",
"country": "中国"
},
"score": null
}
二、核心依赖库选择与准备
在 C# 中处理 JSON,你主要有两个选择:
Newtonsoft.Json(Json.NET):功能强大、生态成熟、兼容性好,是 .NET Framework 和早期 .NET Core 项目的首选。System.Text.Json:.NET Core 3.0+ /.NET 5+ 内置的高性能库,无需额外依赖,是新项目的推荐选择。
安装依赖
-
对于
Newtonsoft.Json:在 Visual Studio 中,右键点击项目 -> 管理 NuGet 程序包 -> 搜索Newtonsoft.Json-> 安装最新稳定版。或使用 Package Manager Console:
Install-Package Newtonsoft.Json
-
对于
System.Text.Json:它已内置在 .NET Core 3.0, .NET 5, .NET 6, .NET 7, .NET 8 等框架中,无需安装任何额外包。只需在代码中引用System.Text.Json命名空间即可。
三、基础概念:JSON 与 C# 对象的映射关系
JSON 的数据结构与 C# 的数据类型可以天然地对应起来,这是序列化和反序列化的基础。
| JSON 数据类型 | C# 对应数据类型 (示例) |
|---|---|
| 对象 (Object) | class, struct |
| 数组 (Array) | List<T>, T[] |
| 字符串 (String) | string |
| 数字 (Number) | int, long, double, decimal |
| 布尔值 (Boolean) | bool |
null | null (对应可空类型,如 int?, string) |
示例:定义映射 JSON 的 C# 实体类
假设我们有如下 JSON 数据:
{
"Name": "张三",
"Age": 25,
"IsStudent": false,
"Hobbies": ["篮球", "编程"],
"Address": {
"City": "北京",
"Street": "中关村大街"
},
"Score": 95.5
}
我们可以定义对应的 C# 类:
// 嵌套对象 Address
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
// 主对象 User
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
public List<string> Hobbies { get; set; } // 数组对应 List<string>
public Address Address { get; set; } // 嵌套对象
public decimal? Score { get; set; } // 可空类型,对应 JSON 的 null
}
四、基础操作:序列化与反序列化
序列化(Serialization)是将 C# 对象转换为 JSON 字符串的过程。反序列化(Deserialization)是将 JSON 字符串转换回 C# 对象的过程。
1. 使用 Newtonsoft.Json
序列化 (Object to JSON)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 1. 构建 C# 对象实例
var user = new User
{
Name = "张三",
Age = 25,
IsStudent = false,
Hobbies = new List<string> { "篮球", "编程" },
Address = new Address { City = "北京", Street = "中关村大街" },
Score = 95.5m
};
// 2. 序列化
// Formatting.Indented 用于生成带缩进的、可读性好的 JSON 字符串
string jsonString = JsonConvert.SerializeObject(user, Formatting.Indented);
// 3. 输出结果
Console.WriteLine(jsonString);
}
}
输出结果将是格式化后的 JSON 字符串,与上文示例一致。
反序列化 (JSON to Object)
using Newtonsoft.Json;
using System;
class Program
{
static void Main()
{
// 1. JSON 字符串
string jsonString = @"{
""Name"": ""李四"",
""Age"": 30,
""IsStudent"": true,
""Hobbies"": [""读书"", ""跑步""],
""Address"": {
""City"": ""上海"",
""Street"": ""外滩大道""
},
""Score"": null
}";
// 2. 反序列化
User user = JsonConvert.DeserializeObject<User>(jsonString);
// 3. 访问对象属性
Console.WriteLine($"姓名: {user.Name}");
Console.WriteLine($"年龄: {user.Age}");
Console.WriteLine($"城市: {user.Address.City}");
Console.WriteLine($"分数: {user.Score ?? "无分数"}"); // 处理可空类型
}
}
2. 使用 System.Text.Json
序列化 (Object to JSON)
using System;
using System.Collections.Generic;
using System.Text.Json;
class Program
{
static void Main()
{
// 1. 构建 C# 对象实例 (同上)
var user = new User
{
Name = "张三",
Age = 25,
IsStudent = false,
Hobbies = new List<string> { "篮球", "编程" },
Address = new Address { City = "北京", Street = "中关村大街" },
Score = 95.5m
};
// 2. 配置序列化选项
var options = new JsonSerializerOptions
{
WriteIndented = true, // 格式化输出
// 解决中文被转义为 Unicode 的问题
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
// 3. 序列化
string jsonString = JsonSerializer.Serialize(user, options);
// 4. 输出结果
Console.WriteLine(jsonString);
}
}
反序列化 (JSON to Object)
using System;
using System.Text.Json;
class Program
{
static void Main()
{
// 1. JSON 字符串 (同上)
string jsonString = @"{
""Name"": ""李四"",
""Age"": 30,
""IsStudent"": true,
""Hobbies"": [""读书"", ""跑步""],
""Address"": {
""City"": ""上海"",
""Street"": ""外滩大道""
},
""Score"": null
}";
// 2. 配置反序列化选项
var options = new JsonSerializerOptions
{
// 忽略 JSON 键名的大小写,增强兼容性
PropertyNameCaseInsensitive = true
};
// 3. 反序列化
User user = JsonSerializer.Deserialize<User>(jsonString, options);
// 4. 访问对象属性 (同上)
Console.WriteLine($"姓名: {user.Name}");
Console.WriteLine($"爱好: {string.Join(", ", user.Hobbies)}");
}
}
五、高级场景处理
1. 键名映射(JSON 与 C# 属性名不一致)
当 JSON 键名(如 user_name)与 C# 命名规范(如 UserName)不一致时,需要显式指定映射关系。
-
Newtonsoft.Json使用[JsonProperty]特性:
public class User
{
[JsonProperty("user_name")]
public string Name { get; set; }
[JsonProperty("user_age")]
public int Age { get; set; }
}
-
System.Text.Json使用[JsonPropertyName]特性:using System.Text.Json.Serialization; public class User { [JsonPropertyName("user_name")] public string Name { get; set; } [JsonPropertyName("user_age")] public int Age { get; set; } }
2. 忽略指定属性
序列化时,有时需要忽略某些敏感信息(如密码)或无需传输的属性。
-
Newtonsoft.Json使用[JsonIgnore]特性:public class User { public string Name { get; set; } [JsonIgnore] public string Password { get; set; } // 序列化时将被忽略 } -
System.Text.Json使用[JsonIgnore]特性:using System.Text.Json.Serialization; public class User { public string Name { get; set; } [JsonIgnore] public string Password { get; set; } // 序列化时将被忽略 }
3. 处理日期时间
JSON 本身没有日期时间类型,通常以字符串("2024-05-20")或时间戳(1716220800000)表示。
-
Newtonsoft.Jsonvar user = new { BirthDate = new DateTime(2000, 1, 1) }; // 序列化为指定格式的字符串 string json = JsonConvert.SerializeObject(user, new JsonSerializerSettings { DateFormatString = "yyyy-MM-dd", Formatting = Formatting.Indented }); // 输出: {"BirthDate":"2000-01-01"} -
System.Text.Json(.NET 6+)var user = new { BirthDate = new DateTime(2000, 1, 1) }; var options = new JsonSerializerOptions { WriteIndented = true, DateFormatString = "yyyy-MM-dd" // 直接指定日期格式 }; string json = JsonSerializer.Serialize(user, options); // 输出: {"BirthDate":"2000-01-01"}
4. 动态解析 JSON(无需实体类)
在不确定 JSON 结构或结构频繁变化时,可以将其解析为动态对象或字典。
-
Newtonsoft.Json- 动态对象 (dynamic)string jsonString = @"{ ""name"": ""王五"", ""age"": 28 }"; dynamic dynamicUser = JsonConvert.DeserializeObject(jsonString); Console.WriteLine(dynamicUser.name); // 输出: 王五 Console.WriteLine(dynamicUser.age); // 输出: 28 -
System.Text.Json-JsonDocument(推荐)JsonDocument提供了一种高效、低分配的方式来读取 JSON 数据,适合临时查询。string jsonString = @"{ ""name"": ""王五"", ""age"": 28, ""address"": { ""city"": ""广州"" } }"; // 使用 'using' 语句确保资源被释放 using JsonDocument doc = JsonDocument.Parse(jsonString); JsonElement root = doc.RootElement; string name = root.GetProperty("name").GetString(); int age = root.GetProperty("age").GetInt32(); string city = root.GetProperty("address").GetProperty("city").GetString(); Console.WriteLine($"{name}, {age}, {city}"); // 输出: 王五, 28, 广州
5. 处理 JSON 文件
读写 JSON 配置文件是常见需求。
using System.IO;
using Newtonsoft.Json; // 或 using System.Text.Json;
public class Config
{
public string ApiKey { get; set; }
public int Timeout { get; set; }
}
// 写入 JSON 文件
var config = new Config { ApiKey = "123456", Timeout = 30 };
string json = JsonConvert.SerializeObject(config, Formatting.Indented);
File.WriteAllText("appsettings.json", json, Encoding.UTF8);
// 读取 JSON 文件
string jsonFromFile = File.ReadAllText("appsettings.json", Encoding.UTF8);
Config loadedConfig = JsonConvert.DeserializeObject<Config>(jsonFromFile);
Console.WriteLine(loadedConfig.ApiKey);
System.Text.Json 的用法类似,只需替换 JsonConvert 为 JsonSerializer 并使用相应的 Options。
六、常见问题与注意事项
-
中文乱码:
Newtonsoft.Json默认处理较好。System.Text.Json需要配置Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping来避免中文被转义为 Unicode。- 读写文件时,始终指定编码为
Encoding.UTF8。
-
可空类型:
- JSON 中的
null值必须映射到 C# 的可空类型(如int?,string,decimal?),否则反序列化会失败。
- JSON 中的
-
键名大小写:
Newtonsoft.Json默认忽略大小写。System.Text.Json默认区分大小写,建议在反序列化时设置PropertyNameCaseInsensitive = true以提高兼容性。
-
循环引用:
- 当对象之间存在循环引用(A 包含 B,B 包含 A)时,直接序列化会导致无限递归和内存溢出。
Newtonsoft.Json:new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }System.Text.Json(.NET 5+):new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles }
-
枚举处理:
- 默认情况下,枚举会被序列化为其 underlying type(通常是
int)。 - 若需序列化为字符串,需使用特性:
Newtonsoft.Json:[JsonConverter(typeof(StringEnumConverter))]System.Text.Json:[JsonConverter(typeof(JsonStringEnumConverter))]
- 默认情况下,枚举会被序列化为其 underlying type(通常是
七、库的选择建议
| 特性 | Newtonsoft.Json (Json.NET) | System.Text.Json |
|---|---|---|
| 性能 | 良好 | 优秀 (通常更快,内存占用更低) |
| 依赖 | 需要安装 NuGet 包 | 内置,无需额外依赖 |
| .NET 版本 | .NET Framework, .NET Core, .NET 5+ | .NET Core 3.0+, .NET 5+ |
| 功能丰富度 | 极高,API 灵活,支持大量高级场景 | 高,.NET 6+ 版本功能已非常完善 |
| 生态与社区 | 非常成熟,问题解决方案多 | 快速成长,官方支持力度大 |
选择建议:
- 新项目 (.NET 5+):优先选择
System.Text.Json。它性能优异,与框架深度集成,是未来的发展方向。 - 维护旧项目 (.NET Framework):继续使用
Newtonsoft.Json,它能提供最稳定和全面的支持。 - 需要复杂自定义序列化逻辑:
Newtonsoft.Json的 API 可能更灵活,更容易实现某些复杂场景。但System.Text.Json在最新版本中也在不断追赶。
总结
本文详细介绍了 C# 中处理 JSON 的两大主流库 Newtonsoft.Json 和 System.Text.Json。从环境准备、基础概念、核心的序列化与反序列化操作,到键名映射、日期处理等高级场景,再到常见问题排查和库的选择建议,都进行了全面的覆盖。
希望对大家有所帮助。感谢大家的关注和点赞。

被折叠的 条评论
为什么被折叠?



