使用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中记录具体类型信息:
- 修改反序列化配置:
var options = new JsonSerializerSettings(){ ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new List<Newtonsoft.Json.JsonConverter> { new StringEnumConverter() }, TypeNameHandling = TypeNameHandling.Auto };
- 在基类的
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,可以编写自定义转换器,手动指定接口对应的具体实现类:
- 实现自定义转换器:
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); } }
- 在反序列化配置中添加该转换器:
var options = new JsonSerializerSettings(){ ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new List<Newtonsoft.Json.JsonConverter> { new StringEnumConverter(), new ServiceableBusPayloadConverter() } };
这种方式可以完全控制反序列化时的类型映射,避免安全风险,同时也能解决序列化时字段丢失的问题。
内容的提问来源于stack exchange,提问作者Malekai
相关产品推荐
相关产品推荐

