资讯动态

C# JSON 序列化与反序列化实战:Newtonsoft.Json 与 System.Text.Json 对比及避坑指南

发布时间:2026/10/18 9:03:13 来源:尧图企业网站定制
简介JsonDemo.rar 是一份面向 C# 开发者、尤其是初涉 .NET 数据交换场景的学习者准备的 JSON 操作示例工程围绕 Newtonsoft.JsonJson.NET库展开帮助读者理解序列化、反序列化、动态解析与自定义序列化行为等核心用法。压缩包共 31 个文件约 3.58MB以 dll 与 xml 承载 Newtonsoft.Json 依赖及其文档cs 源码给出 Person、ParameterByJson 等示例类与主程序逻辑config、csproj、sln 则构成可直接打开运行的工程骨架另附 nupkg、md、json 等辅助文件。资源已有 743 人学习下载说明其在同类入门示例中具备一定参考价值。读者可借此获得一套结构完整的可编译项目对照源码理解 JsonConvert 的调用方式、特性标注对字段的控制以及 JsonSerializerSettings 的配置思路从而在实际项目中更高效地完成 JSON 数据处理。1. 从一份 JsonDemo.rar 说起C# 里把 JSON 玩明白到底要几步很多人第一次在 C# 里处理 JSON都是被一段接口返回值逼出来的明明看着像键值对JsonConvert.DeserializeObject一跑就抛异常或者字段全是 null。这份 JsonDemo.rar 就是冲着这个场景来的——它把 C# 里 JSON 的序列化、反序列化、嵌套对象、数组、日期格式、命名策略这些高频操作打包成一个能直接跑的控制台示例集。适合刚接触 C# 后端或桌面开发、需要快速把 JSON 读写跑通的人也适合想回头补一补 System.Text.Json 和 Newtonsoft.Json 差异的老手。下面按「能干什么 → 怎么跑 → 怎么改」的顺序拆一遍。2. 项目定位与技术选型为什么同时用两套 JSON 库2.1 这个 Demo 到底覆盖了哪些 JSON 场景从压缩包名字和关键词「C# Json」能判断这不是一个业务系统而是一个以演示为目的的代码集合。它要解决的是开发者在真实项目里最常撞上的几类问题把对象转成 JSON 字符串写文件、把接口返回的 JSON 字符串还原成对象、处理对象里嵌套对象和对象数组、控制日期和枚举的序列化格式、以及字段名大小写不一致时怎么映射。常见做法是每个场景单独一个方法放在Program.cs里按序号调用这样新手可以逐段注释掉再运行观察输出变化。我一般会把每个场景的输入和输出都打印到控制台方便对照。项目里通常还会带一个Models文件夹放几个用于演示的实体类比如用户、订单、地址这种有嵌套关系的结构因为扁平对象演示不出 JSON 的坑。技术栈上C# 控制台项目 .NET SDK 是基本盘JSON 库大概率是 Newtonsoft.Json也就是 Json.NET和 System.Text.Json 两套都出现。原因很直接老项目里 Newtonsoft.Json 存量巨大新项目 .NET Core 之后官方主推 System.Text.Json两套 API 长得像但行为有差异放在一起对比才能记住。2.2 Newtonsoft.Json 与 System.Text.Json 的取舍选型这件事在 Demo 里体现为两套写法并存。Newtonsoft.Json 的优势是宽容度高、功能全JsonConvert.SerializeObject一行就能用日期默认走 ISO 8601遇到循环引用还能配置ReferenceLoopHandling。System.Text.Json 的优势是性能好、官方内置、不引第三方包但默认行为更严格比如属性名默认按原样输出、反序列化时对大小写敏感、遇到不认识的字段直接忽略而不是报错。在 Demo 里对比这两套最直观的差异是命名策略。Newtonsoft 用CamelCasePropertyNamesContractResolver或[JsonProperty]特性System.Text.Json 用JsonNamingPolicy.CamelCase或[JsonPropertyName]特性。下面这段是两套写法并排的示例// Newtonsoft.Json 写法 var settings new JsonSerializerSettings { // 驼峰命名适配前端习惯 ContractResolver new CamelCasePropertyNamesContractResolver(), // 日期统一格式避免时区歧义 DateFormatString yyyy-MM-dd HH:mm:ss }; string json1 JsonConvert.SerializeObject(user, settings); // System.Text.Json 写法 var options new JsonOptions { // 驼峰命名策略 PropertyNamingPolicy JsonNamingPolicy.CamelCase, // 缩进输出方便调试 WriteIndented true }; string json2 JsonSerializer.Serialize(user, options);逻辑说明JsonSerializerSettings是 Newtonsoft 的全局配置入口ContractResolver决定属性名怎么映射DateFormatString决定 DateTime 输出成什么样。JsonSerializerOptions是 System.Text.Json 的对应物PropertyNamingPolicy控制命名WriteIndented控制是否格式化。参数上要注意DateFormatString只影响序列化输出反序列化时 Newtonsoft 能自动识别多种日期格式而 System.Text.Json 对日期格式更挑剔格式不匹配会直接抛JsonException。提示如果项目里已经引了 Newtonsoft.Json不要为了「官方内置」强行换 System.Text.Json两套混用容易出现同一字段序列化结果不一致的问题。3. 跑起来环境准备与第一个可运行示例3.1 环境与依赖还原拿到 JsonDemo.rar 后先解压到一个没有中文和空格的路径比如D:\code\JsonDemo。然后确认本机装了 .NET SDK命令行执行dotnet --version能看到版本号即可。如果项目是 .NET Framework 版本则需要 Visual Studio 打开.sln文件如果是 .NET Core/.NET 5 版本直接命令行就能跑。依赖还原这一步如果项目用了 Newtonsoft.Json.csproj里会有PackageReference执行dotnet restore会自动拉包。如果没网或者包源有问题可以在 NuGet 配置里换成可用源。常见做法是项目根目录放一个nuget.config指定包源地址避免全局配置被改乱。# 进入项目目录 cd D:\code\JsonDemo # 还原依赖 dotnet restore # 编译并运行 dotnet run逻辑说明dotnet restore负责把.csproj里声明的包下载到本地缓存dotnet run会先编译再执行。如果restore报找不到包先看包名和版本号是否写错再看网络和包源。参数上dotnet run默认跑当前目录的项目如果解决方案里有多个项目要加--project指定。3.2 第一个序列化与反序列化示例跑通环境后第一个要看的示例通常是「对象转 JSON 再转回对象」。这段代码是整个 Demo 的地基后面所有场景都是它的变体。// 定义一个用于演示的实体 public class User { public int Id { get; set; } public string Name { get; set; } public DateTime CreatedAt { get; set; } } // 序列化对象 - JSON 字符串 var user new User { Id 1, Name A同学, CreatedAt new DateTime(2024, 5, 1, 10, 30, 0) }; string json JsonConvert.SerializeObject(user); Console.WriteLine(json); // 反序列化JSON 字符串 - 对象 string input {\Id\:2,\Name\:\某开发者\,\CreatedAt\:\2024-05-02T08:00:00\}; User parsed JsonConvert.DeserializeObjectUser(input); Console.WriteLine(parsed.Name);逻辑说明SerializeObject把对象按属性名和值拼成 JSON 字符串DeserializeObjectUser把字符串按目标类型还原。参数上要注意反序列化时 JSON 里的字段名必须和类属性名匹配默认区分大小写如果 JSON 里是id而类里是IdNewtonsoft 默认能匹配System.Text.Json 默认不能需要配置PropertyNameCaseInsensitive true。注意日期字段是翻车高发区。DateTime序列化出来带T和时区信息如果对方系统只认yyyy-MM-dd就要在配置里统一格式否则反序列化时直接抛异常。4. 核心模块拆解嵌套对象、数组与命名策略4.1 嵌套对象与对象数组的处理真实接口返回的 JSON 很少是扁平的通常是对象套对象、对象套数组。Demo 里这部分一般会定义一个Order类里面包含User和ListOrderItem用来演示嵌套结构怎么序列化和反序列化。public class Order { public string OrderNo { get; set; } public User Buyer { get; set; } public ListOrderItem Items { get; set; } } public class OrderItem { public string ProductName { get; set; } public int Quantity { get; set; } } var order new Order { OrderNo NO20240501001, Buyer new User { Id 1, Name A同学 }, Items new ListOrderItem { new OrderItem { ProductName 键盘, Quantity 2 }, new OrderItem { ProductName 鼠标, Quantity 1 } } }; string orderJson JsonConvert.SerializeObject(order, Formatting.Indented); Console.WriteLine(orderJson);逻辑说明Formatting.Indented让输出的 JSON 带缩进方便人眼检查嵌套层级。反序列化时DeserializeObjectOrder(orderJson)会自动把Buyer还原成User对象、把Items还原成ListOrderItem前提是类结构和 JSON 层级一一对应。参数上如果 JSON 里数组元素类型不一致反序列化会失败这时候要么用JArray手动解析要么定义更宽泛的类型。常见坑是数组为空和字段缺失。JSON 里Items是null还是[]反序列化后一个是null一个是空列表业务代码里如果直接foreach就会空引用。我一般会在属性上给默认值或者在反序列化后做一次判空。4.2 命名策略与自定义转换器命名策略解决的是 C# 属性名和 JSON 字段名不一致的问题。比如 C# 里叫UserName接口返回的是user_name这时候要么加特性要么全局配置策略。public class UserDto { // 用特性精确指定 JSON 字段名 [JsonProperty(user_name)] public string UserName { get; set; } // 枚举序列化成字符串而不是数字 [JsonConverter(typeof(StringEnumConverter))] public UserStatus Status { get; set; } } public enum UserStatus { Active, Disabled }逻辑说明[JsonProperty(user_name)]告诉序列化器这个属性对应 JSON 里的user_name[JsonConverter(typeof(StringEnumConverter))]让枚举输出成Active而不是0。参数上StringEnumConverter还可以指定是否忽略大小写反序列化时active和Active都能识别。如果项目里大量字段都是下划线风格用全局的SnakeCaseNamingPolicy更省事不用每个属性都加特性。提示自定义转换器是处理特殊格式的后悔药。比如时间戳、金额分转元、布尔值用 0/1 表示都可以写一个继承JsonConverter的类注册到全局配置里避免在每个属性上重复写逻辑。5. 避坑与排查JSON 处理里最容易翻车的几件事5.1 反序列化后字段全是 null现象接口返回的 JSON 字符串肉眼看着没问题DeserializeObject也不报错但对象属性全是 null。原因通常是字段名大小写不匹配或者 JSON 层级和类结构对不上。Newtonsoft 默认对大小写不敏感但 System.Text.Json 默认敏感另外如果 JSON 是{ data: { name: x } }而类里直接是Name属性就会对不上。解决方式是先打印原始 JSON 确认层级再检查类结构必要时用JsonPropertyName或JsonProperty显式指定。5.2 日期格式导致反序列化抛异常现象序列化时日期是2024-05-01T10:30:00反序列化时对方传的是2024/05/01 10:30:00直接抛JsonReaderException。原因是日期格式不匹配且没有配置宽容的日期解析。解决方式是在 Newtonsoft 里用DateParseHandling配合IsoDateTimeConverter指定多种可接受格式或者干脆把日期字段定义成string业务层再自己转。5.3 循环引用导致序列化无限递归现象两个实体互相引用比如Order里有UserUser里又有ListOrder序列化时直接栈溢出。原因是序列化器沿着引用链无限展开。解决方式是在 Newtonsoft 里配置ReferenceLoopHandling.Ignore或者用[JsonIgnore]忽略反向引用属性。System.Text.Json 默认就会检测循环引用并抛异常不会无限递归但需要显式配置ReferenceHandler.IgnoreCycles才能跳过。5.4 大数字精度丢失现象JSON 里有个long类型的 ID反序列化后末几位变成 0。原因是某些场景下数字被当成double处理超过 15 位有效数字就丢精度。解决方式是确保目标属性是long或decimal不要用doubleNewtonsoft 里可以用FloatParseHandling.Decimal强制按 decimal 解析。5.5 空字符串与 null 的混淆现象JSON 里字段是反序列化后属性是空字符串字段缺失时属性是 null。业务代码里如果只判 null 不判空字符串就会漏掉一种情况。解决方式是在属性上给默认值或者在反序列化后统一做一次规范化把空字符串转成 null 或反之看业务约定。6. 二次开发把这套 Demo 改成自己的 JSON 工具类6.1 封装一个通用的 JSON 帮助类Demo 跑通之后下一步就是把它变成项目里能复用的工具。我一般会封装一个静态类把序列化和反序列化的常用配置固化下来业务代码只调一个方法。public static class JsonHelper { private static readonly JsonSerializerSettings Settings new JsonSerializerSettings { // 驼峰命名适配前端 ContractResolver new CamelCasePropertyNamesContractResolver(), // 日期统一格式 DateFormatString yyyy-MM-dd HH:mm:ss, // 忽略循环引用 ReferenceLoopHandling ReferenceLoopHandling.Ignore, // null 值不输出 NullValueHandling NullValueHandling.Ignore }; public static string ToJson(object obj) { return JsonConvert.SerializeObject(obj, Settings); } public static T FromJsonT(string json) { return JsonConvert.DeserializeObjectT(json, Settings); } }逻辑说明Settings是静态只读的避免每次调用都重新构造配置对象减少开销。ToJson和FromJson是泛型方法调用方不用关心配置细节。参数上NullValueHandling.Ignore会让值为 null 的属性不出现在 JSON 里如果对方要求字段必须存在就去掉这一行。ReferenceLoopHandling.Ignore会跳过循环引用但要注意这可能导致部分数据丢失适合展示层不适合需要完整数据的场景。6.2 用单元测试验证序列化行为改完工具类之后别急着在业务里用先写几个单元测试把边界情况固定下来。我一般会测这几类正常对象往返、嵌套对象往返、日期格式、空值、数组为空、字段名大小写。测试框架用 xUnit 或 NUnit 都行关键是让每次改配置后能快速回归。[Fact] public void Serialize_And_Deserialize_Should_Keep_Value() { var user new User { Id 1, Name A同学, CreatedAt new DateTime(2024, 5, 1) }; string json JsonHelper.ToJson(user); var parsed JsonHelper.FromJsonUser(json); Assert.Equal(user.Id, parsed.Id); Assert.Equal(user.Name, parsed.Name); }逻辑说明这个测试验证往返一致性如果哪天有人改了全局配置导致字段名或日期格式变化测试会立刻失败。参数上Assert.Equal对 DateTime 比较的是完整值如果序列化时丢了毫秒这里就会挂正好能暴露问题。6.3 从 Demo 到项目的迁移习惯最后说一个我自己的习惯每次从 Demo 往正式项目搬 JSON 代码我都会先确认三件事——目标框架版本、JSON 库版本、以及对方接口的实际返回样例。这三样对不上Demo 里跑得再顺搬过去也可能翻车。从那以后我每次接新接口都强制先用真实返回的 JSON 字符串跑一遍反序列化确认字段全对上再写业务逻辑。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑