最省事的方式是定义匹配的C#嵌套类,用JsonSerializer.Deserialize()自动填充;字段名需对齐或启用PropertyNameCaseInsensitive;数组用List;动态结构改用JsonDocument手动导航。

用 JsonSerializer.Deserialize<t>()</t> 直接映射多维嵌套结构
只要 JSON 层级明确、字段稳定,最省事的方式就是定义匹配的 C# 类型,让 System.Text.Json 自动填充。它不依赖第三方库,性能好,且 .NET 5+ 默认支持深层嵌套。
常见错误现象:JsonSerializer.Deserialize<object>()</object> 返回空对象或 null —— 因为没指定具体类型,反序列化器无法推断字段名和嵌套关系。
- 必须定义完整嵌套类,比如 JSON 里有
"data": {"items": [{"id": 1}]},就得写三层类:Data、Items、Item - 字段名大小写要对齐:默认要求 PascalCase 匹配 JSON 的 camelCase,可通过
JsonSerializerOptions.PropertyNameCaseInsensitive = true宽松处理 - 遇到数组嵌套(如
"children": [{...}, {...}]),对应 C# 字段必须是List<t></t>或T[],不能是object或JToken
遇到动态键名或不确定结构时,改用 JsonDocument 手动导航
当 JSON 键名是运行时生成的(比如 {"2024-01": {...}, "2024-02": {...}}),或者某些字段可能缺失/类型不一致,强类型反序列化会失败,这时得退到只读 DOM 模式。
使用场景:配置文件含日期/ID 作 key、API 返回结果结构随参数变化、日志类 JSON 中字段不固定。
-
JsonDocument.Parse()不分配字符串副本,内存友好,但必须Dispose() - 用
RootElement.GetProperty("data").GetProperty("items")[0].GetProperty("id").GetInt32()逐层取值,路径错一步就抛KeyNotFoundException - 别直接对
GetProperty("xxx")的结果调ToString()—— 它返回的是 JSON 片段字符串,不是原始值;要用GetInt32()、GetString()等显式转换
JsonSerializerOptions 关键配置项怎么选
默认配置在多数场景下够用,但一旦出现日期格式、循环引用、null 值处理问题,就得调整选项。
容易踩的坑:ReferenceHandler.Preserve 开启后,反序列化出来的对象带 $id/$ref 元数据,但若目标类没加 [JsonInclude] 或没公开 setter,仍会失败。
-
PropertyNameCaseInsensitive = true:解决前端传 camelCase、C# 写 PascalCase 的命名冲突 -
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull:序列化时跳过 null 字段,避免 API 多传无意义字段 - 处理时间:加
Converters.Add(new JsonStringEnumConverter())解析字符串枚举;日期建议统一用DateTimeOffset,避免本地时区干扰
为什么不用 Newtonsoft.Json?兼容性与迁移成本
如果你项目还在用 JsonConvert.DeserializeObject<t>()</t>,不是不能用,而是要注意:.NET Core 3.0+ 默认用 System.Text.Json,两者行为差异不小。
典型差异点:Newtonsoft 默认忽略大小写、支持 JsonProperty 别名、能处理非 public 成员;而 System.Text.Json 默认严格匹配、只序列化 public 成员、不支持运行时动态属性。
- 升级时最常崩的是:
Newtonsoft能解析"value": null到可空 int?,System.Text.Json需显式设IgnoreNullValues = false并确保类型可空 - 如果代码里大量用了
JObject/JArray做中间处理,迁移到JsonDocument或JsonElement需重写导航逻辑,不是改个命名空间就行 - 第三方 SDK(如 Azure SDK)已全面切到
System.Text.Json,混用两种解析器可能导致同一 JSON 被解析两次,性能白耗
复杂点在于:没有“通用解法”。结构规整就上强类型;字段动态就用 JsonDocument;历史包袱重就先封住 Newtonsoft 的使用边界,再逐步替换。关键不是选哪个库,而是别让解析逻辑散落在十几个 DeserializeObject 调用里。


















