.NET 6+ 推荐用 System.Text.Json.Deserialize 读取 JSON,需确保类型匹配、启用宽松解析处理注释和尾逗号,动态场景用 JsonDocument,大文件用流式解析避免 OOM。

用 JsonSerializer.Deserialize 读取 JSON 文件最直接,但必须匹配类型定义
如果 JSON 结构固定、字段明确,推荐用 .NET 6+ 内置的 System.Text.Json。它不依赖第三方包,序列化/反序列化路径短、内存占用低。关键点不是“能不能读”,而是“类型对不对”——Deserialize 不会自动忽略多余字段,也不会把 null 字符串转成 0 或 false。
常见错误现象:JsonSerializer.Deserialize 抛出 JsonException: The JSON value could not be converted to xxx,往往是因为类中某个属性类型和 JSON 实际值不兼容(比如 JSON 里是 "123" 字符串,而 C# 属性是 int)。
- 确保目标类所有非可空引用类型字段在 JSON 中有对应值,或声明为
string?、int?等可空类型 - 用
JsonSerializerOptions.PropertyNameCaseInsensitive = true避免大小写不一致导致匹配失败 - 若 JSON 含日期字符串(如
"2024-05-20T14:30:00"),C# 对应属性用DateTime即可,无需手动解析——但前提是格式符合 ISO 8601,默认支持 - 不要用
object或dynamic接收再强转,那会绕过编译期检查,把问题拖到运行时
JSON 文件含注释或尾部逗号?JsonSerializer 默认不认,得开配置
.NET 原生 JsonSerializer 默认只认标准 JSON(RFC 8259),不支持行内注释(//)、块注释(/* */),也不接受对象末尾多一个逗号({"a":1,})。这类文件用默认设置读取会直接抛 JsonException。
解决办法是启用宽松解析:设置 JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip 和 JsonSerializerOptions.AllowTrailingCommas = true。
示例:
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
};
var data = JsonSerializer.Deserialize<Config>(File.ReadAllText("config.json"), options);
注意:这两个选项仅影响反序列化(读),不影响序列化(写);且开启后仍不支持单引号字符串或未加引号的属性名——那些已超出 JSON 标准范畴,得换解析器。
不知道结构、要查字段名或动态改值?用 JsonDocument 更轻量安全
当 JSON 来源不可控(比如配置片段、API 返回片段、用户上传文件),又不想定义完整类时,JsonDocument 是比 JObject(Newtonsoft)更轻、更省内存的选择。它只做只读解析,不构建对象图,适合“查一下就扔”的场景。
常见误用:用 JsonElement.Clone() 多次访问同一节点——其实 JsonElement 是 struct,复制成本极低,但频繁调用 GetProperty 查嵌套字段时,容易漏掉 TryGetProperty 的返回值判断,导致 InvalidOperationException: Cannot access child value on a value of type Undefined。
- 永远先用
TryGetProperty("xxx", out var element)判断字段是否存在,而不是直接root.GetProperty("xxx") - 读取数值用
element.GetInt32()、element.GetDouble(),别用element.ToString()再转——那是把原始 JSON 文本拿过来,可能含引号或空格 -
JsonDocument.Parse后记得Dispose(),或用using语句,否则底层缓冲区不会释放
读大文件时卡死或 OOM?别一次性 File.ReadAllText
用 File.ReadAllText("huge.json") 加载几百 MB 的 JSON 文件,会瞬间分配等量字符串内存,极易触发 GC 压力甚至 OutOfMemoryException。这不是 JSON 解析慢,是字符串加载阶段就崩了。
正确做法是流式处理:FileStream + Utf8JsonReader,边读边解析,内存占用恒定在几 KB 级别。
适用场景:日志聚合 JSON Lines、传感器批量上报数据、导出的数据库快照(每行一个 JSON 对象)。
- 逐行读取用
File.ReadLines("data.jsonl").Select(line => JsonDocument.Parse(line)),比全读入再 Split 安全 - 真正超大单体 JSON(如 GeoJSON)需用
Utf8JsonReader手动推进,跳过不需要的数组/对象层级,只提取关键字段 - 避免在循环里反复 new
JsonSerializerOptions——它可复用,且线程安全
底层原理上,Utf8JsonReader 直接操作字节流,不生成中间字符串;而 JsonSerializer.Deserialize 底层也基于它,只是封装了映射逻辑。所以“为什么不用 Newtonsoft”这种问题,本质是权衡:是否需要支持注释/松散语法/自定义转换器——需要就加包,不需要就用原生。


















