最省事方案是用JsonSerializer.Deserialize()配合匹配C#类,启用PropertyNameCaseInsensitive和AllowReadingFromString等容错选项;字段名不一致、类型变异或动态结构时,才需JObject或JsonDocument手动处理。

用 JsonSerializer.Deserialize<t>()</t> 直接映射嵌套结构最省事
只要 JSON 结构稳定、字段名可预期,定义匹配的 C# 类 + 一行 JsonSerializer.Deserialize<order>(json)</order> 就能拿到最深层的值,不用手动钻层级。
常见错误现象:Deserialize<order>()</order> 返回对象里某层属性是 null,大概率是字段名大小写不一致(JSON 用 camelCase,C# 类默认 PascalCase),或没处理可空字段。
- 加
PropertyNameCaseInsensitive = true到JsonSerializerOptions,全局忽略大小写,比每个字段都打[JsonPropertyName("xxx")]更轻量 - 嵌套数组字段(如
"items": [{ "id": 1 }])必须声明为public List<item> Items { get; set; }</item>,不能用object或JToken - 可能为
null的字符串字段,类型写成string?;时间戳字段优先用DateTimeOffset,避免时区解析失败 - 字段偶尔缺失或类型混乱(比如
"count"有时是数字、有时是字符串),启用NumberHandling = JsonNumberHandling.AllowReadingFromString
什么时候该用 JObject 而不是强类型反序列化
JObject 不是“更简单”的替代方案,而是为特定场景准备的工具:字段名动态生成(如 "2024_q1"、"field_123")、结构随业务参数变化、或需要根据某个字段值(如 "type": "user")决定后续解析路径。
容易踩的坑:JObject.Parse(json)["data"]["items"][0]["user"]["name"].ToString() 这种链式调用,任意一级为空或类型不符就抛 NullReferenceException 或 InvalidOperationException。
- 必须用空条件操作符:比如
obj["customer"]?["name"]?.ToString() - 关键字段取值前加
if (obj["items"]?.GetArrayLength() > 0)判断,别依赖 try-catch 捕获运行时异常 - 需要 LINQ 计算(如求商品总价)或动态增删字段时,
JObject才真正体现价值;纯读取固定结构,它反而增加出错概率
用 JsonDocument 手动导航只读嵌套节点
当 JSON 键名是日期、ID 等运行时生成的字符串(如 {"2024-01": {...}, "2024-02": {...}}),或者某些字段类型/存在性极不稳定,强类型绑定会频繁失败,这时退到 JsonDocument 是合理选择——它内存友好、只读、不分配字符串副本。
常见错误现象:对 GetProperty("xxx") 的结果直接调 ToString(),得到的是带引号的 JSON 片段(如 "John"),不是原始字符串值。
- 必须用显式转换方法:
GetProperty("id").GetInt32()、GetProperty("name").GetString()、GetProperty("active").GetBoolean() - 逐层取值前检查字段是否存在:
if (root.TryGetProperty("data", out var dataElement)) - 用完必须
Dispose(),或用using var doc = JsonDocument.Parse(json);确保释放
别忽略字段命名策略和容错配置的组合影响
很多问题表面看是“解析失败”,实际是几个配置项互相干扰。比如开了 PropertyNameCaseInsensitive = true 却又给字段加了 [JsonPropertyName("user_name")],后者会覆盖前者;又比如启用了 AllowReadingFromString 但没处理好小写字符串布尔值("true"),仍会报错。
真实接口数据永远比文档“活泼”:字段突然消失、数字变字符串、布尔值写成 "1" 或 "false"。这时候硬靠类定义扛不住,得靠 JsonSerializerOptions 的组合拳。
-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull只影响序列化输出,反序列化时不起作用 -
ReadCommentHandling = JsonCommentHandling.Skip生产环境几乎用不到,开了反而掩盖拼写错误(比如把"user_id"写成"user_id/* comment */") - 混合使用
NumberHandling和PropertyNameCaseInsensitive时,务必在测试中覆盖所有字段类型变异场景


















