You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现不依赖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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 11:13:11