System.Text.Json属性多态序列化不生效问题
问题根因
System.Text.Json 默认按属性的声明类型生成序列化契约,不会自动识别属性值的运行时派生类型。你的KrakenSubscribeRequest.Details属性声明为KrakenSubscriptionDetails类型,即使运行时赋值的是派生类KrakenOhlcSubscriptionDetails实例,默认序列化逻辑也只会遍历基类定义的属性,因此派生类的interval字段会丢失。
你之前写的自定义转换器本质是强制按实例运行时类型序列化,只是硬编码了单个类型,无法复用。
解决方案
方案1:通用运行时类型转换器(兼容所有.NET版本,无需修改实体类)
写一个通用的非泛型JsonConverter,不需要为每个多态基类单独实现,支持按属性粒度启用多态序列化,代码如下:
public class RuntimeTypePolymorphicConverter : JsonConverter<object> { public override bool CanConvert(Type typeToConvert) { // 跳过值类型和字符串,避免干扰默认序列化逻辑 return !typeToConvert.IsValueType && typeToConvert != typeof(string); } public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 仅序列化场景可直接抛出未实现异常,若需支持多态反序列化需扩展类型鉴别逻辑 throw new NotSupportedException("多态反序列化需配合类型鉴别规则使用"); } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { // 强制使用实例的实际运行时类型执行序列化 JsonSerializer.Serialize(writer, value, value.GetType(), options); } }
使用方式:
- 实现属性级多态:直接给对应属性加上转换器特性,不影响其他同类型属性的序列化逻辑
[JsonPropertyName("subscription")] [JsonConverter(typeof(RuntimeTypePolymorphicConverter))] public KrakenSubscriptionDetails Details { get; init; } - 全局启用:将转换器加入
JsonSerializerOptions.Converters集合,所有引用类型成员都会按运行时类型序列化var options = new JsonSerializerOptions { WriteIndented = true, Converters = { new RuntimeTypePolymorphicConverter() } };
注意:该方案仅适配序列化场景,反序列化时无类型标识无法自动匹配派生类,需要双向支持请使用方案2。
方案2:官方原生多态配置(.NET 7+ 支持,兼容序列化/反序列化)
你之前的配置位置有误:多态特性不需要加在根类型KrakenSubscribeRequest上,直接加在多态基类KrakenSubscriptionDetails上即可,配置完成后该基类无论作为序列化根还是其他类的属性,都会自动执行多态序列化,无需编写自定义转换器。
配置方式:给基类添加JsonDerivedType特性,注册所有需要支持的派生类
[JsonDerivedType(typeof(KrakenOhlcSubscriptionDetails))] // 存在其他派生类时继续追加特性即可,例:[JsonDerivedType(typeof(KrakenTradeSubscriptionDetails))] public record KrakenSubscriptionDetails { // 类原有代码保持不变 }
仅做序列化使用时不需要指定类型鉴别符,运行原有测试代码即可输出包含interval字段的正确结果。
如果需要支持反序列化,给每个派生类指定唯一类型鉴别符即可,序列化时会自动写入类型标识,反序列化时可自动匹配对应派生类:
[JsonDerivedType(typeof(KrakenOhlcSubscriptionDetails), "ohlc")]
选型建议
- 项目使用.NET 6及以下版本,或不想修改现有实体类定义:选方案1,一次编写即可在所有需要属性级多态序列化的场景复用,不需要为每个类型单独写转换器。
- 项目使用.NET 7及以上版本,或需要支持多态反序列化:选方案2,原生实现性能更高,无需维护额外转换器逻辑。
内容的提问来源于stack exchange,提问作者nop
相关产品推荐
相关产品推荐

