system.text.json 默认不支持私有字段和循环引用,需显式配置includefields、allowreadonlyproperties等选项,并通过自定义converter处理枚举、空字符串、datetime时区等场景。

System.Text.Json 默认不支持私有字段序列化,也不处理循环引用——这不是 bug,是设计选择;想用它替代 Newtonsoft.Json,必须主动补足这些缺口。
序列化私有字段和只读属性要显式配置 JsonSerializerOptions
默认情况下,System.Text.Json 只序列化 public get/set 属性,连 private set 都被忽略。比如:
class Person {
public string Name { get; set; }
private int _age;
public int Age => _age; // 只读属性,无 setter
}
直接 JsonSerializer.Serialize(person) 会丢失 _age 和 Age。解决办法是启用字段访问 + 允许只读属性:
- 添加
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull(可选,控制 null 处理) - 设置
IncludeFields = true让私有字段参与序列化 - 设置
PropertyNamingPolicy = JsonNamingPolicy.CamelCase(如需驼峰) - 关键一步:用
JsonSerializerOptions的GetTypeInfo或自定义 converter 处理只读属性(.NET 6+ 支持AllowReadOnlyProperties = true)
.NET 6 起可直接写:
var options = new JsonSerializerOptions {
IncludeFields = true,
AllowReadOnlyProperties = true,
WriteIndented = true
};
JsonSerializer.Deserialize<t></t> 报错 “Cannot create an instance of type T” 怎么办
常见于反序列化抽象类、接口或无参构造函数被删的类。错误信息通常是:System.NotSupportedException: Cannot create an instance of an interface. 或类似提示。
- 确保目标类型有 public 无参构造函数(哪怕只是
protected,也要加[JsonConstructor]标记) - 接口/抽象类必须配
JsonConverter,不能靠默认行为 - 如果用了 record,注意 .NET 5+ 才支持 record 的位置参数自动绑定;.NET 6+ 才支持
with表达式在反序列化中生效 - 泛型类型如
List<t></t>没问题,但Dictionary<string object></string>中的object值会被当作JsonElement,不是任意 CLR 类型
处理 DateTime 时区和格式容易踩坑
System.Text.Json 默认把 DateTime 当作本地时间序列化成 ISO 8601 格式(如 "2023-04-05T10:30:00"),不带时区标识,反序列化也按本地时区解释——这在跨服务场景下极易出错。
- 统一用
DateTimeOffset替代DateTime,它自带偏移量,序列化结果如"2023-04-05T10:30:00+08:00" - 若必须用
DateTime,通过JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())不起作用;得注册JsonConverter<datetime></datetime>子类,手动控制格式(例如固定为 UTC + "o" 格式) -
options.DateTimeZoneHandling这个属性不存在——那是 Newtonsoft.Json 的,System.Text.Json没有等价项
自定义 Converter 是绕不开的硬需求
比如枚举转字符串、空字符串转 null、或者把 "true"/"false" 字符串反序列化为 bool,都得靠自定义 JsonConverter<t></t>。
- 继承
JsonConverter<t></t>,重写Read()和Write() -
Read()里用ref Utf8JsonReader reader手动跳过 token,避免调reader.GetString()前没判断 token 类型(否则抛JsonException) - 别在 converter 里 new 大对象或做 IO,它会被高频复用
- 注册方式有两种:全局加到
JsonSerializerOptions.Converters,或局部用[JsonConverter(typeof(MyConverter))]贴在字段上
一个典型例子:把空字符串转为 null 的 string converter,必须先判 reader.TokenType == JsonTokenType.String,再取值,否则遇到 null token 会崩。
System.Text.Json 看似轻量,但一旦脱离“public 属性 + DateTime + 基本类型”的舒适区,就得直面 converter、options 配置、版本兼容性这些细节——尤其在升级旧项目时,.NET 5 和 .NET 7 对 AllowReadOnlyProperties、JsonNumber 的支持差异,很容易漏掉。










