⚠️ 声明:本文中的代码尚未经过笔者的完整验证,内容是笔者最近与 AI 讨论、归纳总结
JsonSerializerOptions的使用心得。如有疏漏,欢迎指正。
C# JsonSerializerOptions 使用总结:本地 JSON 配置文件读取的推荐方案
在 C# 中,System.Text.Json.JsonSerializerOptions 是控制 JSON 序列化和反序列化行为的核心配置类。
它可以决定:
- JSON 输出是否格式化;
- 属性名是否区分大小写;
- 是否允许注释;
- 是否允许尾随逗号;
- 是否忽略
null; - 如何处理循环引用;
- 如何处理数字、枚举、自定义类型等。
本文基于一个典型场景:读取本地 JSON 配置文件,总结 JsonSerializerOptions 的常见属性作用,并给出适合本地配置文件的推荐组合。
✦ 架构起点 (Architecture Origin)
JsonSerializerOptions 是 System.Text.Json 中用于配置 JSON 序列化和反序列化行为的类。
常见用法如下:
1 | using System.Text.Json; |
简单来说:
Serialize时,它控制 C# 对象如何变成 JSON;Deserialize时,它控制 JSON 如何变成 C# 对象。
✦ 常见属性分类说明 (Property Classification)
1. 格式化与输出控制
这类属性主要影响生成 JSON 字符串的格式。
WriteIndented
是否格式化输出 JSON。
默认值是 false。
1 | var options = new JsonSerializerOptions |
如果为 false,输出结果通常是一行:
1 | {"name":"张三","age":18} |
如果为 true,输出结果会带缩进:
1 | { |
什么时候使用?
适合:
- 本地配置文件;
- 日志文件;
- 调试输出;
- 需要人工阅读的 JSON。
不太适合:
- 网络接口高频传输;
- 对体积特别敏感的场景。
对于本地配置文件,建议开启:
1 | WriteIndented = true |
Encoder
用于控制字符串中的字符如何转义。
默认情况下,System.Text.Json 可能会把中文转成 Unicode 转义形式,例如:
1 | { |
如果希望中文原样输出,可以设置:
1 | using System.Text.Encodings.Web; |
输出效果:
1 | { |
什么时候使用?
适合:
- 本地配置文件;
- 中文日志;
- 希望 JSON 文件可读性更好的场景。
需要注意的是,UnsafeRelaxedJsonEscaping 名字里带有 Unsafe,主要是因为它放宽了某些 HTML 相关字符的转义规则。
如果 JSON 会被直接嵌入 HTML 页面中,需要谨慎使用。
但对于本地配置文件,一般可以放心使用。
2. 命名策略与大小写控制
这类属性用于处理 JSON 字段名和 C# 属性名之间的匹配关系。
PropertyNameCaseInsensitive
反序列化时,属性名是否忽略大小写。
默认值是 false。
例如 C# 类:
1 | public class AppConfig |
JSON 文件:
1 | { |
如果设置:
1 | PropertyNameCaseInsensitive = true |
那么 "systemName" 可以正确匹配到 SystemName。
什么时候使用?
适合:
- 本地配置文件;
- 第三方 JSON;
- 前后端命名风格不完全一致;
- 希望配置文件对大小写不敏感。
对于本地配置文件,强烈推荐开启:
1 | PropertyNameCaseInsensitive = true |
需要注意的是,它只是不区分大小写。
例如下面这种情况不能仅靠它解决:
1 | { |
这个字段名是 snake_case,而 C# 是 SystemName,两者不只是大小写不同。
这种情况需要使用:
1 | PropertyNamingPolicy |
或者给属性加:
1 | [] |
PropertyNamingPolicy
控制序列化时 C# 属性名如何转换成 JSON 属性名。
例如:
1 | var options = new JsonSerializerOptions |
C#:
1 | public string SystemName { get; set; } |
输出 JSON:
1 | { |
什么时候使用?
适合:
- Web API;
- 前端习惯使用 camelCase 的接口;
- 希望 JSON 字段统一小驼峰命名。
对于本地配置文件,如果只是读取配置,通常不一定需要设置。
如果你的配置文件统一要求使用小驼峰命名,可以设置:
1 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase |
DictionaryKeyPolicy
用于控制字典类型的 key 如何转换。
例如:
1 | Dictionary<string, string> dict = new() |
如果设置:
1 | DictionaryKeyPolicy = JsonNamingPolicy.CamelCase |
序列化后:
1 | { |
什么时候使用?
适合字典 key 也需要统一命名风格的场景。
本地配置文件一般不一定需要设置。
3. 包含与忽略规则
这类属性决定哪些属性会被写入 JSON。
DefaultIgnoreCondition
控制序列化时忽略哪些默认值。
常见值:
1 | JsonIgnoreCondition.Never |
示例:
1 | var options = new JsonSerializerOptions |
如果对象中某个属性为 null,则不会输出到 JSON。
1 | public class AppConfig |
对象:
1 | var config = new AppConfig |
输出:
1 | { |
什么时候使用?
适合:
- Web API 减少响应体积;
- 不希望输出
null的场景。
对于本地配置文件,需要谨慎。
如果你希望配置文件尽可能完整,建议不要忽略 null。
如果你希望配置文件更简洁,可以使用:
1 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull |
IncludeFields
默认情况下,System.Text.Json 只处理属性,不处理字段。
例如:
1 | public class AppConfig |
上面这个是字段,不是属性。
默认情况下不会被序列化或反序列化。
如果想支持字段,需要:
1 | var options = new JsonSerializerOptions |
什么时候使用?
适合:
- 你的配置类使用了 public 字段;
- 老项目里 DTO 不是属性而是字段。
更推荐的方式是使用属性:
1 | public string SystemName { get; set; } |
对于本地配置文件,除非你确实使用 public 字段,否则不建议开启。
IgnoreReadOnlyProperties
是否忽略只读属性。
1 | public class AppConfig |
DisplayName 是只读属性。
如果设置:
1 | IgnoreReadOnlyProperties = true |
序列化时会忽略它。
什么时候使用?
适合:
- 不希望把计算属性写入 JSON;
- 配置文件只保存真正需要配置的值。
对于本地配置文件,可以考虑开启:
1 | IgnoreReadOnlyProperties = true |
4. 宽容解析:本地配置文件最重要的一类配置
本地配置文件通常由人手动编辑。
人工编辑 JSON 时很容易出现:
- 加注释;
- 多写一个逗号;
- 大小写不一致;
- 数字写成字符串。
所以,本地配置文件读取时通常需要更高的容错性。
ReadCommentHandling
控制如何处理 JSON 中的注释。
JSON 标准本身不支持注释,但配置文件中经常需要注释:
1 | { |
默认情况下,System.Text.Json 遇到注释会报错。
如果设置:
1 | ReadCommentHandling = JsonCommentHandling.Skip |
则会跳过注释。
什么时候使用?
非常适合本地配置文件。
强烈推荐:
1 | ReadCommentHandling = JsonCommentHandling.Skip |
AllowTrailingCommas
是否允许尾随逗号。
例如:
1 | { |
最后一项后面多了一个逗号。
标准 JSON 不允许这种写法,但人手动编辑配置文件时很常见。
设置:
1 | AllowTrailingCommas = true |
即可允许这种格式。
什么时候使用?
非常适合本地配置文件。
强烈推荐:
1 | AllowTrailingCommas = true |
NumberHandling
控制数字与字符串之间是否可以宽松转换。
例如配置文件中写了:
1 | { |
但 C# 类是:
1 | public class AppConfig |
默认情况下,字符串 "8080" 不能直接反序列化成 int。
可以设置:
1 | using System.Text.Json.Serialization; |
这样 "8080" 就可以读成整数 8080。
什么时候使用?
适合:
- 第三方 JSON 不规范;
- 配置文件中用户可能把数字加上引号;
- 想提高配置文件容错能力。
对于本地配置文件,可以按需开启:
1 | NumberHandling = JsonNumberHandling.AllowReadingFromString |
如果你希望严格要求配置格式,则不要开启。
5. 对象深度、循环引用与高级配置
MaxDepth
控制 JSON 最大嵌套深度。
默认值通常已经够用。
例如:
1 | var options = new JsonSerializerOptions |
什么时候使用?
适合:
- 防止恶意构造的超深 JSON;
- 限制配置文件复杂度。
普通本地配置文件一般不需要修改。
ReferenceHandler
用于处理对象循环引用。
例如 A 引用 B,B 又引用 A。
常见配置:
1 | ReferenceHandler = ReferenceHandler.IgnoreCycles |
或者:
1 | ReferenceHandler = ReferenceHandler.Preserve |
什么时候使用?
适合:
- Entity Framework Core 实体;
- 对象图中存在双向引用;
- 复杂对象序列化。
本地配置文件一般不需要设置。
配置类应该尽量设计成简单的树形结构,不建议出现循环引用。
Converters
用于注册自定义转换器。
例如:
- 自定义日期格式;
- 枚举字符串转换;
- 特殊类型转换;
- 加密字段转换;
- 自定义配置项格式。
示例:
1 | var options = new JsonSerializerOptions |
这样枚举可以用字符串形式读写:
1 | { |
而不是:
1 | { |
什么时候使用?
本地配置文件中非常常见的一个场景是枚举。
如果配置类中有枚举,推荐添加:
1 | Converters.Add(new JsonStringEnumConverter()); |
这样配置文件更容易阅读和维护。
✦ 本地配置文件读取的推荐组合 (Recommended Combination)
对于本地 JSON 配置文件,最核心的目标是:
- 允许注释;
- 允许尾随逗号;
- 属性名大小写不敏感;
- 输出格式便于人类阅读;
- 中文正常显示;
- 可选支持数字字符串和枚举字符串。
推荐配置如下:
1 | using System.Text.Encodings.Web; |
✦ 完整示例 (Complete Example)
1. 配置文件 config.json
1 | { |
这个 JSON 中包含:
- 单行注释;
- 多行注释;
- 尾随逗号;
- 小驼峰字段名;
- 字符串形式的数字;
- 字符串形式的枚举。
使用默认 JsonSerializerOptions 读取时可能会报错。
使用上面的 LocalConfigOptions 可以正常读取。
2. C# 配置类
1 | public class AppConfig |
3. 读取配置文件
1 | using System.Text.Json; |
输出:
1 | 仓储管理系统 |
4. 写回配置文件
如果程序修改了配置,也可以写回本地文件:
1 | config.EnableLogging = false; |
输出结果类似:
1 | { |
需要注意:System.Text.Json 在反序列化后再序列化时,不会保留原文件中的注释。
也就是说,如果你读取一个带注释的 JSON 文件,再写回去,原来的注释会丢失。
如果你需要完整保留注释、空行、格式等信息,就需要使用专门支持 JSON 编辑的库,或者自己设计配置更新逻辑。
✦ 本地配置文件推荐配置总结 (Summary)
对于读取本地配置文件,最推荐的三个配置是:
1 | ReadCommentHandling = JsonCommentHandling.Skip; |
这三个可以称为本地配置文件读取的”黄金三角”。
它们分别解决:
| 配置项 | 作用 |
|---|---|
ReadCommentHandling = JsonCommentHandling.Skip |
允许配置文件中写注释 |
AllowTrailingCommas = true |
允许最后多一个逗号 |
PropertyNameCaseInsensitive = true |
属性名大小写不敏感 |
如果还需要写回配置文件,建议额外加上:
1 | WriteIndented = true; |
如果希望配置文件更宽容,可以加上:
1 | NumberHandling = JsonNumberHandling.AllowReadingFromString; |
如果有枚举配置项,建议加上:
1 | Converters.Add(new JsonStringEnumConverter()); |
✦ 最终推荐版本 (Final Recommended Version)
如果你的目标是读取和写入本地配置文件,可以使用下面这个版本:
1 | using System.Text.Encodings.Web; |
使用方式:
1 | string json = File.ReadAllText("config.json"); |
✦ 结论 (Conclusion)
JsonSerializerOptions 是 System.Text.Json 中非常重要的配置类。
对于 Web API、日志、缓存、第三方接口和本地配置文件,不同场景应该使用不同的组合。
如果你的目标是读取本地配置文件,推荐重点关注:
1 | ReadCommentHandling |
其中最核心的是:
1 | ReadCommentHandling = JsonCommentHandling.Skip; |
这套配置可以让 JSON 配置文件更接近人工可维护的格式,减少因为注释、大小写或多余逗号导致的读取失败问题。
参考链接