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

C#中如何无需两次反序列化将JSON字符串转为对应正确类型

解决方案

核心采用自定义System.Text.Json多态转换器 + 自动类型映射的方案,同时解决switch case维护成本高、二次反序列化性能损耗两个问题,全程使用官方库无需切换到Newtonsoft.Json。

实现步骤

1. 定义事件类型绑定特性与自动映射机制

新增特性标记每个事件子类对应的EventType,程序启动时自动扫描所有子类生成映射表,后续新增事件类型无需修改核心逻辑:

// 事件类型绑定特性
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class EventMessageAttribute : Attribute
{
    public string EventTypeKey { get; }
    public EventMessageAttribute(string eventTypeKey) => EventTypeKey = eventTypeKey;
}

// 事件类型映射管理器,程序启动时自动扫描注册所有事件类型
public static class EventMessageTypeMapper
{
    private static readonly IReadOnlyDictionary<string, Type> _eventTypeMap;

    static EventMessageTypeMapper()
    {
        // 扫描当前程序集所有BaseMessage的非抽象子类,自动加载映射
        _eventTypeMap = typeof(BaseMessage).Assembly.GetTypes()
            .Where(t => t.IsSubclassOf(typeof(BaseMessage)) && !t.IsAbstract)
            .Select(t => new 
            {
                Attr = t.GetCustomAttribute<EventMessageAttribute>(),
                MessageType = t
            })
            .Where(x => x.Attr != null)
            .ToDictionary(x => x.Attr.EventTypeKey, x => x.MessageType);
    }

    public static Type GetMessageType(string eventTypeKey)
    {
        return _eventTypeMap.TryGetValue(eventTypeKey, out var type) 
            ? type 
            : typeof(BaseMessage); // 未匹配到的返回基类,可按需调整降级逻辑
    }
}

// 新增事件类型示例,仅需加特性+定义属性即可,无需修改其他代码
[EventMessage("trade")]
public class TradeMessage : BaseMessage
{
    [JsonPropertyName("p")]
    public decimal TradePrice { get; init; }
    
    // 其他业务属性省略
}

2. 实现自定义多态反序列化转换器

仅读取一次JSON流,先获取EventType字段匹配目标类型,直接完成全量反序列化,无二次解析开销:

public class BaseMessageConverter : JsonConverter<BaseMessage>
{
    public override BaseMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // 拷贝reader避免读取EventType字段后丢失原数据位置
        var readerCopy = reader;
        using var jsonDoc = JsonDocument.ParseValue(ref readerCopy);
        
        // 读取EventType字段
        if (!jsonDoc.RootElement.TryGetProperty("e", out var eventTypeElem) 
            || string.IsNullOrEmpty(eventTypeElem.GetString()))
        {
            return JsonSerializer.Deserialize<BaseMessage>(ref reader, options);
        }
        
        // 获取对应实际消息类型,直接一次反序列化
        var targetType = EventMessageTypeMapper.GetMessageType(eventTypeElem.GetString()!);
        return (BaseMessage?)JsonSerializer.Deserialize(ref reader, targetType, options);
    }

    public override void Write(Utf8JsonWriter writer, BaseMessage value, JsonSerializerOptions options)
    {
        // 序列化逻辑按需实现,不需要可直接抛出异常
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
}

3. 配置并修改接收逻辑

全局注册转换器,直接从输入流一次反序列化得到对应类型的对象:

// 全局初始化一次序列化配置即可
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
    Converters = { new BaseMessageConverter() }
};

// 优化后的消息接收方法
private async Task ResponseReceived(Stream inputStream) 
{
    // 直接从流反序列化,省去读取完整字符串的内存开销
    var message = await JsonSerializer.DeserializeAsync<BaseMessage>(inputStream, _jsonOptions);
    if(message == null) return;
    
    Console.WriteLine($"Message Type Received: {message.EventType}");
    // 事件推送可搭配中介者模式(如MediatR)直接推送对象,自动匹配对应处理逻辑,无需判断类型
}

方案优势

  • 低维护成本:新增事件类型仅需继承BaseMessage、加EventMessage特性、定义对应属性即可,完全不需要修改核心反序列化、事件分发逻辑,适配无上限的EventType扩展。
  • 高性能:全程仅执行一次反序列化,直接从Stream读取无需中转字符串,使用官方Utf8JsonReader处理性能远高于二次反序列化方案,完全满足每秒数千条消息的吞吐量要求。

内容的提问来源于stack exchange,提问作者Aaron Murray

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 09:36:03