C#操作JSON文件 C#如何读取和写入JSON数据

1次阅读

system.text.json 是 .net core 3.0+ 和 .net 5+ 读写 json 的首选方案,性能优、内存省;需注意类型匹配、大小写一致、空值处理、路径创建、原子写入、datetimezonehandling 及大文件用 jsondocument。

C#操作JSON文件 C#如何读取和写入JSON数据

System.Text.Json 读取 JSON 文件最稳妥

.NET Core 3.0+ 和 .NET 5+ 默认推荐用 System.Text.Json,性能好、内存占用低,且无需额外 NuGet 包。直接调用 JsonSerializer.Deserialize<t>()</t> 即可,但要注意类型必须匹配,否则抛 JsonException

常见错误现象:JsonException: The JSON value could not be converted to XXX,通常是因为 JSON 字段名大小写不一致、字段缺失或类型错配(比如 JSON 里是字符串,C# 类型却是 int)。

  • 确保目标类的属性名与 JSON 字段名一致,或用 [JsonPropertyName("xxx")] 显式标注
  • 对可能为空的字段,用可空类型(如 int?String)或设置 JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNULL
  • 读取失败时先用 File.ReadAllText(path) 打印原始 JSON,确认结构是否符合预期

写入 JSON 文件时别漏掉 JsonSerializerOptions.WriteIndented

默认序列化结果是紧凑格式(无换行缩进),不利于人工查看和调试。启用美化输出只需一行配置:

var options = new JsonSerializerOptions { WriteIndented = true };

但要注意:开启 WriteIndented 会略微增加 CPU 和内存开销,高频写入日志类场景建议关闭;而配置文件、调试导出等场景强烈建议打开。

  • 写入前检查对象是否为 null,否则 Serialize 会返回空字符串而非报错
  • 路径不存在时 File.WriteAllText 不会自动创建目录,需提前调用 Directory.CreateDirectory(Path.GetDirectoryName(path))
  • 避免直接覆盖正在被其他进程读取的 JSON 配置文件,可先写临时文件再原子替换

遇到日期格式混乱就改 DateTimeZoneHandling

C# 的 DateTime 默认序列化为 ISO 8601 格式(如 "2024-05-20T09:30:00"),但时区信息容易丢失或误判。如果 JSON 来源是 JavaScript 或其他语言,常出现 "/Date(1234567890000)/" 这类格式,System.Text.Json 默认不支持解析。

  • 统一用 DateTimeOffset 替代 DateTime 可保留时区偏移
  • 需要兼容旧格式?得手动预处理字符串,或换用 Newtonsoft.Json(它内置 DateTimeZoneHandling 枚举)
  • 序列化时控制格式:通过 JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()) 等扩展转换器

小文件用 Deserialize<t></t>,大文件或流式场景用 JsonDocument

如果 JSON 文件超过几 MB,或者你只关心其中某几个字段(比如只取 "users[0].name"),全量反序列化成对象会浪费内存。这时该用 JsonDocument.Parse() 解析为只读 dom 树,按需提取值:

using var doc = JsonDocument.Parse(jsonString); var name = doc.RootElement.GetProperty("name").GetString();

注意:JsonDocument 必须显式释放(用 using),否则可能引发内存泄漏;它不支持修改,纯读取场景才适用。

  • 无法直接映射到泛型类,适合结构动态、字段不确定的配置解析
  • 嵌套深、层级多时,链式调用 GetProperty() 容易触发 KeyNotFoundException,务必用 TryGetProperty()
  • 从文件读取时,优先用 JsonDocument.Parse(File.OpenRead(path)) 避免一次性加载全部内容到内存

实际项目中,多数配置类场景用 System.Text.Json + 强类型反序列化足够;但只要涉及 JS 互通、复杂日期、或超大 JSON,就得在序列化选项、类型选择和异常兜底上多留一层判断。

text=ZqhQzanResources