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

使用Newtonsoft和System.Text.Json序列化/反序列化含抽象基类的类丢失值

解决ServiceBus事件序列化/反序列化问题

错误原因

你的TestEvent继承自ServiceableBusEvent<IServiceableBusPayload>,反序列化时JSON序列化器遇到Payload字段的接口类型IServiceableBusPayload,无法确定要实例化的具体实现类,因此抛出"无法创建接口/抽象类实例"的异常。同时序列化时,接口类型的字段也可能无法被完整序列化。

解决方案

方案一:使用具体Payload类型作为泛型参数(最简单直接)

修改TestEvent的继承关系,将泛型参数从接口IServiceableBusPayload改为具体的TestEventPayload:

public class TestEvent : ServiceableBusEvent<TestEventPayload>
{
    public const string Topic = "test-event";

    public record TestEventPayload(string Field1, int Field2, int Field3) : IServiceableBusPayload;
}

这样ServiceableBusEvent<T>中的Payload字段类型变为具体类,序列化器可以直接识别并处理,无需额外配置。

方案二:通过TypeNameHandling添加类型信息(需注意安全风险)

如果架构要求必须保留接口作为泛型参数,可以通过Newtonsoft.Json的TypeNameHandling让序列化器在JSON中记录具体类型信息:

  1. 修改反序列化配置:
var options = new JsonSerializerSettings(){
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    Converters = new List<Newtonsoft.Json.JsonConverter> { new StringEnumConverter() },
    TypeNameHandling = TypeNameHandling.Auto
};
  1. 在基类的Payload属性上添加类型处理标记:
public abstract class ServiceableBusEvent<T> : IServiceableBusEvent where T : IServiceableBusPayload
{
    [JsonPropertyName("messageTypeName")]
    public string MessageTypeName { get; set; }

    [JsonPropertyName("createdAt")]
    public DateTime CreatedAt { get; set; }

    [JsonProperty("payload", TypeNameHandling = TypeNameHandling.Auto)]
    public T Payload { get; set; }
}

序列化后的JSON会自动添加$type字段记录Payload的具体类型,反序列化时序列化器会根据该字段实例化正确的类。

⚠️ 注意:TypeNameHandling存在安全风险,如果反序列化不可信的JSON数据,可能被利用注入恶意类型,仅在处理可信内部消息时使用。

方案三:自定义JsonConverter(安全可控)

如果不想依赖TypeNameHandling,可以编写自定义转换器,手动指定接口对应的具体实现类:

  1. 实现自定义转换器:
public class ServiceableBusPayloadConverter : JsonConverter<IServiceableBusPayload>
{
    public override IServiceableBusPayload ReadJson(JsonReader reader, Type objectType, IServiceableBusPayload existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        // 可根据业务逻辑(如MessageTypeName字段)判断具体Payload类型,这里以TestEventPayload为例
        return serializer.Deserialize<TestEvent.TestEventPayload>(reader);
    }

    public override void WriteJson(JsonWriter writer, IServiceableBusPayload value, JsonSerializer serializer)
    {
        // 序列化时直接输出具体类的字段
        serializer.Serialize(writer, value);
    }
}
  1. 在反序列化配置中添加该转换器:
var options = new JsonSerializerSettings(){
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    Converters = new List<Newtonsoft.Json.JsonConverter> { new StringEnumConverter(), new ServiceableBusPayloadConverter() }
};

这种方式可以完全控制反序列化时的类型映射,避免安全风险,同时也能解决序列化时字段丢失的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:20:01