news 2026/10/18 10:09:31

C# JSON 处理实战:Newtonsoft.Json 与 System.Text.Json 选型、避坑与性能调优

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C# JSON 处理实战:Newtonsoft.Json 与 System.Text.Json 选型、避坑与性能调优

简介:JsonDemo.rar 是一份面向 C# 开发者、尤其是初涉 .NET 数据交互场景的学习者准备的 JSON 处理示例工程,围绕 Newtonsoft.Json(Json.NET)库展开,帮助读者理解序列化、反序列化、动态解析与自定义序列化行为等核心用法。压缩包共 31 个文件,约 3.58MB,以 dll 与 xml 依赖库文件、cs 源码、config 配置、csproj 与 sln 工程文件为主,另含 nupkg 包与 json 数据文件,构成一个可直接打开运行的完整项目结构。资源中通过 Person、ParameterByJson 等类与 Program 入口演示了对象与 JSON 字符串的相互转换、JObject 动态取值以及 JsonProperty、JsonIgnore 等特性控制,并借助 JsonSerializerSettings 展示日期格式与命名策略配置。目前已有 743 人学习下载,适合希望快速上手 .NET 平台 JSON 处理、对照源码理解库 API 的开发者参考。

1. 从一份 JsonDemo.rar 说起:C# 里 JSON 处理到底该从哪下手

如果你写过 C#,大概率遇到过这种场景:对接第三方接口,返回一大坨 JSON,字段嵌套三四层,数组里还混着 null;或者本地要存配置、存日志、存缓存,用数据库太重,用纯文本又不好解析。这时候 JSON 几乎是默认答案。但真到动手,问题就来了——用Newtonsoft.Json还是System.Text.Json?反序列化时字段名对不上怎么办?日期格式、枚举、循环引用这些玄学问题怎么破?

这份 JsonDemo.rar 就是围绕这些真实痛点整理的一套 C# JSON 处理示例集合。它不厚,但覆盖了序列化、反序列化、动态解析、配置读写、异常处理这几条主线,适合刚接触 C# JSON 的新手照着跑一遍,也适合有经验的开发者拿来当速查手册。下面我按「先跑通、再理解、最后避坑」的顺序,把这份资源拆开讲清楚。

2. 环境准备与两种主流库的选型:Newtonsoft.Json 和 System.Text.Json 到底用哪个

2.1 先确认你的 .NET 版本和项目类型

拿到 JsonDemo.rar 后别急着双击运行,先看项目文件。C# 的 JSON 生态和 .NET 版本强绑定,选错库会直接编译不过。常见组合是这样的:

.NET 版本推荐 JSON 库说明
.NET Framework 4.5 及以下Newtonsoft.Json内置没有 JSON 支持,必须引第三方
.NET Framework 4.6+Newtonsoft.Json 或 System.Text.Json后者需通过 NuGet 安装
.NET Core 3.1 / .NET 5+System.Text.Json 优先官方内置,性能更好
.NET Standard 2.0两者都行看目标平台

我一般会先打开.csproj文件,看<TargetFramework>那一行。如果是net6.0、net8.0这种,直接用System.Text.Json就行,不用额外装包。如果是net48或者更老的,老老实实上Newtonsoft.Json。

提示:JsonDemo.rar 里如果同时包含两种库的示例,建议先跑和你项目版本匹配的那一套,不要混着抄,混用容易在命名空间上翻车。

2.2 安装依赖:NuGet 命令行和包管理器两种方式

假设你决定用Newtonsoft.Json,安装方式有两种。命令行适合自动化,图形界面适合新手。

# 方式一:Package Manager Console 里执行 Install-Package Newtonsoft.Json -Version 13.0.3 # 方式二:dotnet CLI(.NET Core / .NET 5+ 项目) dotnet add package Newtonsoft.Json --version 13.0.3

如果你用System.Text.Json,.NET Core 3.0 以上是内置的,不需要装。但如果你在 .NET Framework 上想用它,得额外装:

dotnet add package System.Text.Json --version 8.0.0

参数说明:--version后面跟的是包版本号,不写会默认拉最新版。生产项目建议锁定版本,避免某天自动升级后 API 行为变了导致线上事故。JsonDemo.rar 里的示例代码如果引用了某个特定版本才有的方法,版本对不上会直接报MissingMethodException。

2.3 两种库的核心 API 对照

选型不能只看性能,还要看 API 顺手程度。我把最常用的几个操作列出来对比:

操作Newtonsoft.JsonSystem.Text.Json
序列化JsonConvert.SerializeObject(obj)JsonSerializer.Serialize(obj)
反序列化JsonConvert.DeserializeObject<T>(json)JsonSerializer.Deserialize<T>(json)
动态解析JObject.Parse(json)JsonDocument.Parse(json)
忽略 nullNullValueHandling.IgnoreDefaultIgnoreCondition = WhenWritingNull
日期格式DateFormatStringJsonSerializerOptions里配 Converter

从表里能看出来,Newtonsoft.Json的 API 更直观,尤其是动态解析那块,JObject用起来像操作字典一样顺手。System.Text.Json性能确实好,官方基准测试里序列化速度快不少,但它的JsonDocument是只读的,想改结构得用JsonNode,而且默认对大小写敏感,字段名对不上直接给你反序列化成 null,这个坑后面会细说。

我个人的习惯是:新项目、纯 API 对接、对性能有要求,用System.Text.Json;老项目维护、需要频繁动态改 JSON 结构、团队更熟悉 Newtonsoft,就继续用Newtonsoft.Json。JsonDemo.rar 里两套都有,你可以对照着跑,感受一下差异。

3. 序列化与反序列化的核心操作:从对象到 JSON 再回来

3.1 定义一个能安全序列化的模型类

先看最基础的场景:把一个 C# 对象转成 JSON 字符串。假设我们有个用户信息类:

public class UserInfo { public int Id { get; set; } public string UserName { get; set; } public string Email { get; set; } public DateTime RegisterTime { get; set; } public List<string> Tags { get; set; } }

这里有几个细节要注意。属性必须是public的,且带get和set,否则序列化时会被跳过。字段(field)默认不序列化,除非你显式标记。List<string>这种集合类型没问题,但如果是Dictionary<int, string>,Newtonsoft 和 System.Text.Json 对 key 的处理方式不一样,前者支持非字符串 key,后者在默认配置下会报错。

var user = new UserInfo { Id = 1001, UserName = "A同学", Email = "a@example.com", RegisterTime = DateTime.Now, Tags = new List<string> { "vip", "active" } }; // Newtonsoft.Json 写法 string json1 = JsonConvert.SerializeObject(user, Formatting.Indented); // System.Text.Json 写法 var options = new JsonSerializerOptions { WriteIndented = true }; string json2 = JsonSerializer.Serialize(user, options);

Formatting.Indented和WriteIndented = true都是让输出带缩进,方便人看。生产环境如果追求体积小,可以去掉,能省不少字节。JsonDemo.rar 里两种写法都有示例,跑一遍就能看到输出差异。

3.2 反序列化时字段名对不上的三种解法

这是最高频的翻车点。接口返回的 JSON 字段是user_name,你类里写的是UserName,直接反序列化,UserName就是 null。三种解法:

第一种,用特性标注映射关系:

public class UserInfo { [JsonProperty("user_name")] // Newtonsoft public string UserName { get; set; } }
public class UserInfo { [JsonPropertyName("user_name")] // System.Text.Json public string UserName { get; set; } }

第二种,全局配置命名策略。Newtonsoft 用SnakeCaseNamingStrategy,System.Text.Json 用JsonNamingPolicy.SnakeCaseLower(.NET 8 开始内置)。

// Newtonsoft 全局蛇形命名 var settings = new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() } }; var user = JsonConvert.DeserializeObject<UserInfo>(json, settings);
// System.Text.Json 全局蛇形命名(.NET 8+) var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; var user = JsonSerializer.Deserialize<UserInfo>(json, options);

第三种,如果只是临时解析几个字段,不想建类,用动态方式:

// Newtonsoft 动态解析 var obj = JObject.Parse(json); string name = obj["user_name"]?.ToString(); // System.Text.Json 动态解析 using var doc = JsonDocument.Parse(json); string name2 = doc.RootElement.GetProperty("user_name").GetString();

三种方式各有适用场景。类结构稳定、字段多,用特性或全局策略;只是取一两个值,动态解析更省事。JsonDemo.rar 里对这三种都有对应示例文件,建议都跑一遍,心里有个谱。

3.3 日期、枚举、null 值的处理参数

日期格式是另一个高频坑。C# 的DateTime序列化出来默认是 ISO 8601 带时区的格式,但有些接口要求yyyy-MM-dd HH:mm:ss,不带 T 不带 Z。Newtonsoft 可以这样配:

var settings = new JsonSerializerSettings { DateFormatString = "yyyy-MM-dd HH:mm:ss", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore };

System.Text.Json 没有直接的DateFormatString,需要自定义 Converter:

public class CustomDateTimeConverter : JsonConverter<DateTime> { public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateTime.Parse(reader.GetString()); public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss")); }

枚举默认序列化成数字,想输出字符串名字,Newtonsoft 加StringEnumConverter,System.Text.Json 加JsonStringEnumConverter。null 值默认会输出"field": null,如果接口方不接受 null 字段,就得配忽略策略。这些参数在 JsonDemo.rar 的配置示例里都能找到,改一改就能用。

4. 动态解析与配置文件读写:不建类也能玩转 JSON

4.1 JObject 和 JsonNode 的增删改查

不是所有 JSON 都值得建一个类。比如读配置文件、处理结构不固定的第三方回调,动态解析更灵活。Newtonsoft 的JObject用起来像操作嵌套字典:

var root = JObject.Parse(json); // 读 string city = root["address"]?["city"]?.ToString(); // 改 root["address"]["city"] = "某市"; // 增 root["newField"] = "newValue"; // 删 root["obsolete"]?.Remove(); // 转回字符串 string updated = root.ToString(Formatting.Indented);

System.Text.Json 在 .NET 6 之后提供了JsonNode,用法类似但类型更严格:

var node = JsonNode.Parse(json); string city = node["address"]?["city"]?.GetValue<string>(); node["address"]["city"] = "某市"; node["newField"] = "newValue"; node["obsolete"]?.AsObject().Remove(); string updated = node.ToJsonString(new JsonSerializerOptions { WriteIndented = true });

注意JsonNode的索引返回的是JsonNode,取值要调GetValue<T>(),直接转 string 会编译不过。这个设计比 Newtonsoft 啰嗦一点,但类型安全更好,不容易出现运行时转换异常。

4.2 用 JSON 做应用配置的读写封装

很多小工具不需要数据库,配置直接存 JSON 文件。我一般会封装一个简单的配置管理器:

public class AppConfig { public string ApiBaseUrl { get; set; } public int TimeoutSeconds { get; set; } public Dictionary<string, string> Headers { get; set; } } public static class ConfigManager { private static readonly string ConfigPath = "config.json"; public static AppConfig Load() { if (!File.Exists(ConfigPath)) return new AppConfig { TimeoutSeconds = 30, Headers = new Dictionary<string, string>() }; string json = File.ReadAllText(ConfigPath); return JsonSerializer.Deserialize<AppConfig>(json) ?? new AppConfig(); } public static void Save(AppConfig config) { var options = new JsonSerializerOptions { WriteIndented = true }; string json = JsonSerializer.Serialize(config, options); File.WriteAllText(ConfigPath, json); } }

这段代码的关键点:读之前先判断文件是否存在,不存在返回默认配置,避免第一次运行直接抛异常。Deserialize可能返回 null,用??兜底。写的时候带缩进,方便手动改。JsonDemo.rar 里有类似的配置读写示例,可以直接拿去改成自己需要的字段。

4.3 大 JSON 文件的流式读取

如果 JSON 文件几十兆甚至上百兆,一次性ReadAllText再Parse会吃掉大量内存,严重时直接 OOM。这时候要用流式读取。Newtonsoft 提供JsonTextReader,System.Text.Json 提供Utf8JsonReader:

using var stream = File.OpenRead("bigdata.json"); using var reader = new StreamReader(stream); using var jsonReader = new JsonTextReader(reader); while (jsonReader.Read()) { if (jsonReader.TokenType == JsonToken.PropertyName && jsonReader.Value?.ToString() == "targetField") { jsonReader.Read(); Console.WriteLine(jsonReader.Value); } }

流式读取的核心思路是不把整个文档加载进内存,而是逐个 token 扫描,遇到目标字段就取值。代价是代码复杂,不能随机访问。JsonDemo.rar 里如果包含大文件示例,建议重点看这一块,普通小文件用不上,但数据量上来之后这是保命技能。

5. 避坑与排查:C# JSON 处理中最容易翻车的五个点

5.1 反序列化后字段全是 null

现象:JSON 字符串明明有值,反序列化出来的对象属性全是 null。

原因:字段名大小写不匹配,或者命名策略没配对。System.Text.Json 默认大小写敏感,UserName和userName在它眼里是两个不同的东西。

解决:要么给属性加[JsonPropertyName]特性,要么全局配PropertyNameCaseInsensitive = true。Newtonsoft 默认大小写不敏感,但如果你手动改了ContractResolver,也可能出现这个问题。

5.2 循环引用导致序列化直接崩

现象:两个对象互相引用,序列化时抛JsonSerializationException,提示检测到循环引用。

原因:A 对象里有 B,B 对象里又引用 A,序列化器无限递归。

解决:Newtonsoft 配ReferenceLoopHandling.Ignore,System.Text.Json 配ReferenceHandler.IgnoreCycles。更好的做法是从模型设计上避免双向导航属性,或者用 DTO 裁剪掉不需要的引用。

5.3 日期时间少了几小时

现象:序列化出来的时间比实际时间少了 8 小时,或者反序列化后时间对不上。

原因:时区处理不一致。DateTime.Now是本地时间,DateTime.UtcNow是 UTC 时间,序列化时如果带上了时区偏移,对方按 UTC 解析就会差几个小时。

解决:统一用DateTimeOffset或者统一用 UTC 时间序列化,展示时再转本地。JsonDemo.rar 里如果有日期示例,注意看它用的是哪种。

5.4 枚举值对不上号

现象:接口返回的枚举是字符串"Active",你类里定义的是int枚举,反序列化直接报错。

原因:枚举的序列化格式不匹配。默认情况下 Newtonsoft 和 System.Text.Json 都把枚举当数字处理。

解决:加StringEnumConverter或JsonStringEnumConverter。如果字符串值和枚举名不完全一致,还得自定义 Converter 做映射。

5.5 大数字精度丢失

现象:JSON 里有个long类型的 ID,反序列化后末几位变成了 0。

原因:JavaScript 的Number精度限制,但 C# 本身没这个问题。出现这种情况通常是中间经过了 JS 层,或者用了double去接long。

解决:C# 侧用long或decimal接,不要用double。如果 JSON 里的数字超过了long范围,用BigInteger或者当字符串处理。JsonDemo.rar 里如果有 ID 字段示例,注意看它的类型定义。

6. 进阶技巧:自定义 Converter 和性能调优的实战参数

6.1 写一个能处理复杂类型的自定义 Converter

有些类型系统自带 Converter 搞不定,比如IPAddress、Version、或者你自己定义的强类型 ID。这时候得自己写。以System.Text.Json为例:

public class VersionConverter : JsonConverter<Version> { public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { string value = reader.GetString(); return string.IsNullOrEmpty(value) ? null : new Version(value); } public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) { writer.WriteStringValue(value?.ToString()); } } // 注册 var options = new JsonSerializerOptions(); options.Converters.Add(new VersionConverter());

关键点:Read里要处理 null 和空字符串,否则遇到"version": null会直接抛异常。Write里也要判空。注册方式可以是全局options.Converters.Add,也可以是给单个属性加[JsonConverter(typeof(VersionConverter))]。JsonDemo.rar 里如果有自定义 Converter 示例,重点看它怎么处理边界值。

6.2 性能调优:几个能省内存的参数

如果 JSON 处理量大,这几个参数值得调:

参数作用建议值
DefaultBufferSize流式读写缓冲区大小16384 或 32768
WriteIndented是否缩进生产环境 false
DefaultIgnoreCondition忽略 null 或默认值WhenWritingNull
PropertyNameCaseInsensitive大小写不敏感按接口实际情况
NumberHandling数字处理策略按需,默认即可

DefaultBufferSize默认是 16KB,如果单条 JSON 特别大,适当调大能减少扩容次数。WriteIndented在生产环境一定要关,缩进带来的空白字符能占不少体积。DefaultIgnoreCondition设成WhenWritingNull能省掉大量"field": null。

6.3 用 Source Generator 减少反射开销

.NET 7 之后System.Text.Json支持 Source Generator,能在编译期生成序列化代码,避免运行时反射,启动速度和内存都有改善。用法是定义一个JsonSerializerContext:

[JsonSerializable(typeof(UserInfo))] [JsonSerializable(typeof(AppConfig))] internal partial class MyJsonContext : JsonSerializerContext { }

然后序列化时用JsonSerializer.Serialize(user, MyJsonContext.Default.UserInfo)。这样生成的代码是强类型的,没有反射,AOT 场景下也能用。代价是每加一个类型都要在 Context 里注册,类型多了维护成本会上来。JsonDemo.rar 里如果包含 Source Generator 示例,建议在 .NET 7+ 项目里跑,低版本不支持。

6.4 一个我常用的验证习惯

每次改完 JSON 相关的配置或模型,我不会只看编译过不过,而是会跑一个最小验证:拿一条真实数据,序列化再反序列化,对比关键字段是否一致。这个习惯帮我拦下过好几次字段名写错、日期格式配错的问题。具体做法很简单:

var original = new UserInfo { Id = 1, UserName = "test", RegisterTime = DateTime.Now }; string json = JsonSerializer.Serialize(original); var restored = JsonSerializer.Deserialize<UserInfo>(json); if (restored.UserName != original.UserName) throw new Exception("字段名映射有问题");

从那以后我每次动 JSON 配置,都强制走一遍这个往返验证,比事后在日志里大海捞针强得多。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 10:08:50

Keras实现MADDPG:多智能体强化学习的集中训练与分散执行

简介&#xff1a;基于Keras实现的多智能体深度确定性策略梯度&#xff08;MADDPG&#xff09;代码包&#xff0c;面向强化学习研究者与Python开发者&#xff0c;适用于机器人协同、多人博弈、交通控制等连续动作多智能体场景。压缩包共21个文件、大小7.86MB&#xff0c;其中12个…

作者头像 李华
网站建设 2026/10/18 10:06:05

MATLAB实现Wolf算法计算最大李雅普诺夫指数:混沌时间序列分析实战

简介&#xff1a;这份资源围绕李雅普诺夫指数与沃夫&#xff08;Wolf&#xff09;算法的计算实现展开&#xff0c;面向学习非线性动力学、混沌时间序列分析及系统稳定性研究的高校学生与科研人员。压缩包内共1个文件&#xff0c;为MATLAB脚本&#xff08;.m格式&#xff09;&am…

作者头像 李华
网站建设 2026/10/18 10:03:10

金融AI毕设实战:可执行预测系统设计与全流程验证

简介&#xff1a;这是一套面向计算机专业本科生的毕业设计与课程作业级股票智能预测系统实现&#xff0c;聚焦人工智能在金融时序预测中的落地应用&#xff0c;帮助学习者掌握从数据采集、特征工程到模型训练与可视化展示的完整开发流程。资源包共2000个文件&#xff0c;涵盖80…

作者头像 李华
网站建设 2026/10/18 10:03:02

C#多平台偶像动态监听与人脸去重图库系统

简介&#xff1a;这是一套面向C#开发者与社交平台数据自动化管理爱好者的技术实践源码&#xff0c;聚焦SNH48系偶像动态监听与图片智能归档场景。项目基于C#多语言框架构建&#xff0c;整合微博、口袋48、B站等主流平台API监听能力&#xff0c;并通过QQ机器人实时推送新动态&am…

作者头像 李华
网站建设 2026/10/18 10:01:53

C# BACnet协议栈与模拟器源码实战:从编解码到二次开发

简介&#xff1a;这份资源由C#编写&#xff0c;包含两个可直接运行的BACnet客户端程序及配套模拟器&#xff0c;面向工业自动化、楼宇自控领域的开发者与学习者&#xff0c;用于解决BACnet协议读写调试缺乏现成示例的问题。第一个程序演示模拟量&#xff08;如温度&#xff09;…

作者头像 李华
网站建设 2026/10/18 10:01:16

局域网组网入门:从冲突域和广播域到设备选型与配置

简介&#xff1a;一份计算机网络课程设计报告&#xff0c;主题为局域网组网&#xff0c;适合高校计算机、人工智能等专业学生参考。压缩包仅含1个Word文档&#xff08;docx格式&#xff09;&#xff0c;约562KB&#xff0c;以完整报告正文与目录结构呈现&#xff0c;便于阅读、…

作者头像 李华