推荐使用 .NET Core 3.0+ 自带的 System.Text.Json,因其无第三方依赖、序列化路径统一、原生 UTF-8 友好;迁移时注意大小写敏感、null/空数组区分及日期格式限制。

用 System.Text.Json 解析 JSON 字符串最稳妥
默认推荐用 .NET Core 3.0+ 自带的 System.Text.Json,不是因为多先进,而是它不依赖第三方包、序列化/反序列化路径统一、且对 UTF-8 原生友好。如果项目还在用 Newtonsoft.Json(也就是 Json.NET),除非有历史兼容需求,否则没必要继续扛着——它在 .NET 6+ 里已显冗余。
常见错误现象:JsonSerializer.Deserialize 报 JsonException: The input does not contain any JSON tokens,往往是因为传入了 null 或空字符串,而不是格式问题。
- 确保
json是非 null、非空白字符串,建议先用string.IsNullOrWhiteSpace()检查 - 类型
T的属性名必须与 JSON 字段名匹配(默认区分大小写),或用[JsonPropertyName("xxx")]显式标注 - 若 JSON 含日期字段,注意
DateTime默认只支持 ISO 8601 格式(如"2023-10-05T14:30:00Z"),遇到"2023/10/05"会直接抛异常
解析不确定结构的 JSON 用 JsonDocument 或 JsonElement
当 JSON 结构动态、字段可选、或者你只想取其中一两个字段(比如日志里抽 "error.code"),别硬套强类型反序列化——容易因缺失字段崩掉,也浪费内存。
使用场景:API 返回结果外层固定、内层 data 字段结构多变;配置文件部分字段可选;调试时快速读取某字段值。
-
JsonDocument.Parse(json)返回只读树结构,适合一次解析、多次读取;用完必须.Dispose(),否则可能内存泄漏 -
JsonElement更轻量,适合单次提取,比如root.GetProperty("data").GetProperty("items")[0].GetProperty("id").GetInt32() - 访问不存在的属性会抛
KeyNotFoundException,务必用TryGetProperty("xxx", out var elem)判断存在性
Newtonsoft.Json 还值得留吗?看这三点
如果你正维护老项目,或依赖某些它独有的特性(比如 JToken 动态遍历、自定义 JsonConverter 太深),可以暂留。但新代码不建议引入。
性能影响:在 .NET 5+ 上,System.Text.Json 反序列化速度通常快 1.5–2 倍,分配内存少 30%+;Newtonsoft.Json 的 dynamic 解析尤其慢,且容易触发 GC 压力。
-
JObject.Parse()返回的是托管对象,字段访问不校验,拼错名也不会报错,运行时才崩——调试成本高 -
JsonConvert.DeserializeObject默认忽略大小写,而() System.Text.Json默认严格匹配,迁移时容易漏改属性名 - 若用了
TypeNameHandling.Auto,反序列化可能执行任意类型创建,存在远程代码执行风险(虽需配合恶意输入,但已不被推荐)
嵌套数组和空值处理最容易翻车
JSON 里 "items": null 和 "items": [] 是两回事,C# 类型系统没法自动消解这种语义差异。强类型反序列化时,List 对应 null 会直接报错,对应空数组则正常。
参数差异:用 System.Text.Json 时, JsonSerializerOptions.Default 不处理 null 集合;想让 null 自动转为空集合,得配 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull 并配合构造函数或可空集合类型(如 IEnumerable + 手动判空)。
- 字段声明成
public List能防 null,但无法阻止反序列化把- Items { get; set; } = new();
null写进去(除非关掉PropertyNameCaseInsensitive并加校验逻辑) - 更稳的做法是接收
JsonElement,再用element.ValueKind == JsonValueKind.Array分支处理 - 前端传
"count": ""或"count": "abc"给int字段?System.Text.Json直接炸,不会默默转 0——这是好事,说明契约没对齐,得前端改,别在 C# 层兜底
底层原理其实不玄乎:所有解析器最终都得把字节流按 JSON RFC 8259 规则做词法分析(tokenize)和语法分析(parse)。差别只在中间表示层——是建对象树、还是填字段、还是交由用户手动游标遍历。选哪个,取决于你愿不愿意为灵活性多写几行判空代码。











