如何将JSON反序列化为基类集合并转换为对应具体派生类实例
System.Text.Json 默认不启用多态反序列化能力,直接指定反序列化目标为基类集合时,序列化器不会主动识别派生类型,所有集合元素都会被构造为基类实例,因此针对派生类的类型判断永远不会命中。
另外贴出的代码存在两处基础语法问题:类定义缺少class关键字、类和属性均未声明public访问修饰符,默认访问级别会导致序列化器无法正常访问成员,调试时需要先修正。
方案1:使用.NET 7+ 原生多态序列化配置(推荐,无第三方依赖)
官方从.NET 7开始为System.Text.Json提供了原生多态支持,只需要在基类上标记特性配置派生类型映射即可:[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")] [JsonDerivedType(typeof(ExtendedBaseClassA), "classA")] [JsonDerivedType(typeof(ExtendedBaseClassB), "classB")] public class BaseClass // 可定义为抽象类 { public int PropA { get; set; } } public class ExtendedBaseClassA : BaseClass { public int PropB { get; set; } public int PropC { get; set; } } public class ExtendedBaseClassB : BaseClass { public int PropD { get; set; } public int PropE { get; set; } }配置完成后,序列化时会自动在JSON中插入
$type字段存储类型鉴别值,反序列化时不需要修改原有反序列化逻辑,就能自动将元素转换为对应的派生类实例。注意序列化和反序列化两端必须使用完全一致的多态配置,如果JSON字段使用小驼峰命名,只需要在JsonSerializerOptions中配置PropertyNamingPolicy = JsonNamingPolicy.CamelCase即可,不需要修改类属性定义。方案2:使用Newtonsoft.Json(Json.NET)实现
如果项目可以引入第三方序列化库,Newtonsoft.Json默认就支持多态反序列化,只需要开启类型名称处理配置即可:
安装对应Nuget包后,修改反序列化代码:IEnumerable<BaseClass> objects = JsonConvert.DeserializeObject<IEnumerable<BaseClass>>( serializeMessage, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto } );该方案同样要求序列化时也开启
TypeNameHandling.Auto配置,生成的JSON会自动携带类型元数据,反序列化时自动还原为对应派生类型。方案3:自定义类型转换器(适用于无法修改序列化输出的场景)
如果拿到的JSON字符串本身没有携带任何类型鉴别字段,也无法调整序列化端的逻辑,可以自定义JSON转换器,根据JSON中存在的特征属性判断实际类型:public class BaseClassConverter : JsonConverter<BaseClass> { public override BaseClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; // 根据独有属性判断匹配的派生类型 if (root.TryGetProperty("PropB", out _) && root.TryGetProperty("PropC", out _)) { return JsonSerializer.Deserialize<ExtendedBaseClassA>(root.GetRawText(), options); } if (root.TryGetProperty("PropD", out _) && root.TryGetProperty("PropE", out _)) { return JsonSerializer.Deserialize<ExtendedBaseClassB>(root.GetRawText(), options); } // 无匹配特征时兜底返回基类实例 return JsonSerializer.Deserialize<BaseClass>(root.GetRawText(), options); } public override void Write(Utf8JsonWriter writer, BaseClass value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, value.GetType(), options); } }反序列化时将转换器加入配置即可生效:
var options = new JsonSerializerOptions(); options.Converters.Add(new BaseClassConverter()); // 小驼峰配置可按需开启 // options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; IEnumerable<BaseClass> objects = JsonSerializer.Deserialize<IEnumerable<BaseClass>>(serializeMessage, options);这种方案不需要序列化端做任何修改,完全靠反序列化端的规则判断类型,适配性最强。
内容的提问来源于stack exchange,提问作者Guy E

