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
相关产品推荐
相关产品推荐

