System.Text.Json是否有类似CustomCreationConverter的实现方案?
System.Text.Json 没有提供和 Newtonsoft.Json CustomCreationConverter<T> 完全同名的开箱即用组件,但完全可以通过自定义 JsonConverter<T> 实现一模一样的能力,你原来写在 Create 方法里的自定义实例化逻辑,基本可以零改动迁移。
通用实现方案
你可以先写一个和 Newtonsoft CustomCreationConverter<T> 签名对齐的泛型基类,后续所有需要自定义创建逻辑的转换器直接继承这个基类、重写Create方法即可,不需要重复写属性映射的逻辑。
兼容 .NET Core 3.0+ 所有版本的基类实现如下:
using System.Text.Json; using System.Text.Json.Serialization; public abstract class CustomCreationConverter<T> : JsonConverter<T> where T : class { // 对齐Newtonsoft的Create方法定义,你的自定义实例化逻辑直接重写这个方法即可 protected abstract T Create(Type objectType, ref Utf8JsonReader reader, JsonSerializerOptions options); public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("当前转换器仅支持对象类型的自定义创建逻辑"); } // 执行你自定义的实例创建逻辑 T instance = Create(typeToConvert, ref reader, options); // 自动将JSON字段填充到已创建的实例上,不需要手动逐属性映射 using JsonDocument doc = JsonDocument.ParseValue(ref reader); var serializeOptions = new JsonSerializerOptions(options); // 注入空转换器规避递归调用导致的栈溢出 serializeOptions.Converters.Add(new EmptyConvertor<T>()); JsonSerializer.Deserialize(serializeOptions, doc.RootElement, instance); return instance; } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { // 写入逻辑复用默认序列化规则即可 var serializeOptions = new JsonSerializerOptions(options); serializeOptions.Converters.Add(new EmptyConvertor<T>()); JsonSerializer.Serialize(writer, value, typeof(T), serializeOptions); } // 空转换器用于阻断递归调用 private class EmptyConvertor<TTarget> : JsonConverter<TTarget> where TTarget : class { public override TTarget? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => null; public override void Write(Utf8JsonWriter writer, TTarget value, JsonSerializerOptions options) {} } }
如果你使用的是 .NET 8 及以上版本,可以用内置的 PopulateObject 方法简化Read逻辑,性能表现更好:
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("当前转换器仅支持对象类型的自定义创建逻辑"); } T instance = Create(typeToConvert, ref reader, options); // 直接将JSON内容填充到已创建的实例,不需要中转JsonDocument JsonSerializer.PopulateObject(ref reader, instance, options); return instance; }
使用示例
用法和你之前用 Newtonsoft 的 CustomCreationConverter 几乎没有区别,比如你需要在创建实例时自动填充默认值、从DI容器取服务、根据JSON字段判断实例类型,直接写在Create方法里即可:
// 示例业务实体 public class User { public int Id { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } } // 自定义创建转换器 public class UserCreationConverter : CustomCreationConverter<User> { protected override User Create(Type objectType, ref Utf8JsonReader reader, JsonSerializerOptions options) { // 这里放你的自定义逻辑,比如: // 1. 从服务容器获取已初始化的实例 // 2. 读取JSON里的type字段判断要创建的子类类型 // 3. 给不需要从JSON反序列化的字段赋默认值 return new User { CreateTime = DateTime.Now }; } } // 注册转换器后即可正常使用 var options = new JsonSerializerOptions(); options.Converters.Add(new UserCreationConverter()); // 反序列化时会自动走你定义的Create逻辑 var user = JsonSerializer.Deserialize<User>("{\"Id\":1,\"Name\":\"张三\"}", options);
注意事项
- 如果你的Create逻辑需要读取JSON中的字段做判断(比如多态反序列化场景),注意读取字段后要将
Utf8JsonReader的位置复位,避免后续属性填充时丢失字段;更稳妥的方式是先用JsonDocument解析出需要判断的字段,再做实例创建。 - 如果你的创建逻辑不需要读取JSON内容,可以把基类的Create方法签名简化为
protected abstract T Create(Type objectType),和Newtonsoft的签名完全一致,迁移成本为0。
内容的提问来源于stack exchange,提问作者Pop Young
相关产品推荐
相关产品推荐

