如何实现不依赖Base类型的高性能线程安全Newtonsoft.Json多态转换器?
实现线程安全且不依赖Base类型的高性能PolymorphicConverter
需求背景
给定如下C#多态类型层次结构:
[JsonConverter(typeof(PolymorphicConverter<Base>))] public record Base { private Base() {} public record A(string Value) : Base; public record B(int Foobar) : Base; public record C(Base Recursive) : Base; }
期望序列化后的JSON格式(以类型A为例):
{ "type": "A", "content": { "value": "jalla" } }
现有实现虽然性能达标,但存在线程安全问题:
using System; using Newtonsoft.Json; Base a = new Base.A("foobar"); var json = JsonConvert.SerializeObject(a); Console.WriteLine(json); [JsonConverter(typeof(PolymorphicConverter<Base>))] public record Base { private Base() {} public record A(string Value) : Base; public record B(int Foobar) : Base; public record C(Base Recursive) : Base; } public class PolymorphicConverter<TBase> : JsonConverter where TBase : class { public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { var type = value?.GetType() ?? throw new NotImplementedException(); var converter = serializer.ContractResolver.ResolveContract(type).Converter; serializer.ContractResolver.ResolveContract(type).Converter = null; writer.WriteStartObject(); writer.WritePropertyName("type"); writer.WriteValue(type.Name); writer.WritePropertyName("content"); serializer.Serialize(writer, value); serializer.ContractResolver.ResolveContract(type).Converter = converter; writer.WriteEndObject(); } public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override bool CanConvert(Type objectType) { return typeof(TBase).IsAssignableFrom(objectType); } }
问题根源
现有实现的线程不安全问题在于:直接修改了serializer.ContractResolver返回的契约实例的Converter属性。契约解析器通常是全局共享的(比如默认的DefaultContractResolver),多线程并发序列化时,会出现多个线程同时修改同一个契约实例的情况,引发竞态条件和不可预期的行为。
线程安全的实现方案
核心思路是不修改全局共享的契约实例,通过创建临时契约解析器包装类,在当前序列化上下文内临时覆盖转换器设置,避免影响全局状态。以下是优化后的完整实现:
using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; public class PolymorphicConverter<TBase> : JsonConverter where TBase : class { // 线程安全的类型名称缓存,减少重复反射开销 private static readonly System.Collections.Concurrent.ConcurrentDictionary<Type, string> _typeNameCache = new(); public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value == null) { writer.WriteNull(); return; } var actualType = value.GetType(); var typeName = _typeNameCache.GetOrAdd(actualType, t => t.Name); writer.WriteStartObject(); writer.WritePropertyName("type"); writer.WriteValue(typeName); writer.WritePropertyName("content"); // 创建临时契约解析器,仅在当前序列化流程中移除目标类型的转换器 var tempResolver = new TemporaryConverterRemoverResolver(serializer.ContractResolver, actualType); var tempSerializer = new JsonSerializer { ContractResolver = tempResolver, // 复制原序列化器的核心配置,保持行为一致 Formatting = serializer.Formatting, NullValueHandling = serializer.NullValueHandling, DefaultValueHandling = serializer.DefaultValueHandling, ReferenceLoopHandling = serializer.ReferenceLoopHandling, Converters = serializer.Converters }; // 使用临时序列化器序列化内容,避免修改全局契约 tempSerializer.Serialize(writer, value); writer.WriteEndObject(); } public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null!; // 解析type字段获取目标类型 var jObj = Newtonsoft.Json.Linq.JObject.Load(reader); var typeName = jObj["type"]?.ToString() ?? throw new JsonSerializationException("Missing 'type' property"); var targetType = typeof(TBase).Assembly.GetType($"{typeof(TBase).Namespace}.{typeName}") ?? throw new JsonSerializationException($"Unknown type '{typeName}'"); // 解析content字段并反序列化为目标类型 var contentToken = jObj["content"]; return contentToken.ToObject(targetType, serializer)!; } public override bool CanConvert(Type objectType) { return typeof(TBase).IsAssignableFrom(objectType); } // 临时契约解析器:仅对目标类型临时移除转换器,不影响全局状态 private class TemporaryConverterRemoverResolver : IContractResolver { private readonly IContractResolver _innerResolver; private readonly Type _targetType; public TemporaryConverterRemoverResolver(IContractResolver innerResolver, Type targetType) { _innerResolver = innerResolver; _targetType = targetType; } public JsonContract ResolveContract(Type type) { var contract = _innerResolver.ResolveContract(type); if (type == _targetType) { contract.Converter = null; } return contract; } } }
实现优势
- 线程安全:通过临时契约解析器包装类,仅在当前序列化上下文内修改目标类型的契约配置,不会触动全局共享的契约实例,彻底避免多线程竞态条件。
- 高性能:
- 用
ConcurrentDictionary缓存类型名称,减少重复反射操作的开销; - 仅对目标类型的契约做临时修改,其余类型复用原契约解析器的缓存,保持序列化性能。
- 用
- 不依赖Base类型:仅通过泛型参数
TBase做基类约束,无需对具体基类做硬编码,通用性强。 - 完整能力:补充了反序列化逻辑,实现了完整的多态序列化/反序列化支持。
测试验证
使用原有类型结构测试:
Base a = new Base.A("foobar"); Base c = new Base.C(new Base.B(42)); var jsonA = JsonConvert.SerializeObject(a, Formatting.Indented); var jsonC = JsonConvert.SerializeObject(c, Formatting.Indented); Console.WriteLine(jsonA); Console.WriteLine(jsonC); // 反序列化测试 var deserializedA = JsonConvert.DeserializeObject<Base>(jsonA); var deserializedC = JsonConvert.DeserializeObject<Base>(jsonC);
输出的JSON将符合预期格式,且多线程并发序列化时不会出现异常或错误结果。
内容的提问来源于stack exchange,提问作者havarnov
相关产品推荐
相关产品推荐

