如何解决System.Text.Json中JsonConverter泛型形式的解析问题?
关于System.Text.Json转换器与C#泛型限制的问题
问题背景
在自定义System.Text.Json转换器时,调用options.GetConverter(...)返回的是非泛型JsonConverter类型,但实际所有转换器都是继承自它的泛型JsonConverter<T>。由于非泛型类型没有Read/Write方法,不得不放弃内置注册表,转而手动维护Type到序列化委托的字典,这种方式繁琐且冗余。
问题解答
1. 有没有更好的方法解决这个问题?
有三种更高效的替代方案:
方案一:直接使用JsonSerializer内置重载
无需手动处理转换器,直接调用JsonSerializer.Serialize的重载方法,内部已自动完成转换器查找与类型适配:
JsonSerializer.Serialize(writer, value, value.GetType(), options);
如果是处理已知类型,也可以指定类型参数:
JsonSerializer.Serialize<string>(writer, (string)value, options);
方案二:反射调用泛型转换器方法
通过反射获取JsonConverter<T>的Write/Read方法并调用,无需手动维护字典:
public static void SerializeValue(Utf8JsonWriter writer, object? value, Type type, JsonSerializerOptions options) { var converter = options.GetConverter(type); if (converter == null || value == null) { writer.WriteNullValue(); return; } // 获取泛型Write方法并调用 var writeMethod = converter.GetType().GetMethod( nameof(JsonConverter<string>.Write), new[] { typeof(Utf8JsonWriter), type, typeof(JsonSerializerOptions) }); writeMethod?.Invoke(converter, new object[] { writer, value, options }); }
方案三:缓存泛型委托优化性能
如果担心反射性能问题,可以提前为每个Type缓存自动生成的序列化委托,兼顾灵活性与性能:
private static readonly Dictionary<Type, Action<Utf8JsonWriter, object?, JsonSerializerOptions>> _cachedSerializers = new(); public static void SerializeCached(Utf8JsonWriter writer, object? value, Type type, JsonSerializerOptions options) { if (!_cachedSerializers.TryGetValue(type, out var serializer)) { var converterGenericType = typeof(JsonConverter<>).MakeGenericType(type); var writeMethod = converterGenericType.GetMethod( nameof(JsonConverter<string>.Write), new[] { typeof(Utf8JsonWriter), type, typeof(JsonSerializerOptions) }); if (writeMethod == null) { serializer = (w, v, o) => w.WriteNullValue(); } else { // 通过表达式树创建强类型委托 var paramWriter = Expression.Parameter(typeof(Utf8JsonWriter), "writer"); var paramValue = Expression.Parameter(typeof(object), "value"); var paramOptions = Expression.Parameter(typeof(JsonSerializerOptions), "options"); var converterExpr = Expression.Call( typeof(JsonSerializerOptions), nameof(JsonSerializerOptions.GetConverter), null, paramOptions, Expression.Constant(type)); var castConverter = Expression.Convert(converterExpr, converterGenericType); var castValue = Expression.Convert(paramValue, type); var writeCall = Expression.Call(castConverter, writeMethod, paramWriter, castValue, paramOptions); serializer = Expression.Lambda<Action<Utf8JsonWriter, object?, JsonSerializerOptions>>( writeCall, paramWriter, paramValue, paramOptions).Compile(); } _cachedSerializers[type] = serializer; } serializer(writer, value, options); }
2. C#泛型是否真的无法在运行时将非泛型基类解析为泛型实现?
这确实是C#静态类型系统的限制:
- C#泛型是编译时绑定的,泛型类型参数
T必须在编译时确定,而Type对象是运行时的类型信息,因此无法直接将JsonConverter强制转换为JsonConverter<T>(除非编译时已知T)。 - 但可以通过运行时反射或表达式树绕过这个限制,因为运行时可以获取到具体的泛型类型实例信息,进而调用其泛型方法或创建强类型委托。
官方设计上,JsonConverter基类并未暴露公开的Write/Read方法,就是引导开发者使用JsonSerializer静态方法或通过反射处理这类场景。
内容的提问来源于stack exchange,提问作者Exertive Studio
相关产品推荐
相关产品推荐

