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

System.Text.Json多态序列化问题:子类自定义属性无法序列化

解决System.Text.Json多态序列化问题(抽象类无法序列化子类自定义属性)

当使用System.Text.Json序列化包含抽象类属性的对象时,默认只会序列化抽象类定义的属性,子类的自定义属性会丢失。由于抽象类SubData位于独立包中,无法提前通过[JsonDerivedType]特性注册所有可能的派生类,以下是几种可行的动态多态序列化方案:

方案一:自定义JsonConverter处理多态序列化

通过实现JsonConverter<SubData>,在序列化时使用子类的实际类型进行序列化,同时可添加类型鉴别器字段用于反序列化。

public class SubDataConverter : JsonConverter<SubData>
{
    // 反序列化逻辑(服务端需要时实现)
    public override SubData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var doc = JsonDocument.ParseValue(ref reader);
        if (!doc.RootElement.TryGetProperty("$type", out var typeElement))
            throw new JsonException("缺少类型鉴别器$type");

        var targetType = Type.GetType(typeElement.GetString());
        if (targetType == null || !typeof(SubData).IsAssignableFrom(targetType))
            throw new JsonException("无法识别的派生类型");

        return (SubData)JsonSerializer.Deserialize(doc.RootElement.GetRawText(), targetType, options);
    }

    // 序列化逻辑
    public override void Write(Utf8JsonWriter writer, SubData value, JsonSerializerOptions options)
    {
        var actualType = value.GetType();
        writer.WriteStartObject();
        // 添加类型鉴别器,用于反序列化识别子类
        writer.WriteString("$type", actualType.AssemblyQualifiedName);
        
        // 序列化子类的所有属性
        var element = JsonSerializer.SerializeToElement(value, actualType, options);
        foreach (var prop in element.EnumerateObject())
        {
            prop.WriteTo(writer);
        }
        writer.WriteEndObject();
    }
}

使用方式:

void SerializData(Data data)
{
    var options = new JsonSerializerOptions
    {
        Converters = { new SubDataConverter() }
    };
    var serialized = JsonSerializer.Serialize(data, options);
    // 此时serialized包含ExtendedSubData的Prop3属性及$type字段
}

方案二:通过TypeInfoResolver动态注册派生类型

利用DefaultJsonTypeInfoResolver在运行时动态为抽象类添加派生类型,无需修改独立包中的SubData类。

用户在自己的代码中配置序列化选项:

void SerializData(Data data)
{
    var options = new JsonSerializerOptions
    {
        TypeInfoResolver = new DefaultJsonTypeInfoResolver
        {
            Modifiers =
            {
                typeInfo =>
                {
                    // 针对SubData类配置多态
                    if (typeInfo.Type == typeof(SubData))
                    {
                        // 注册用户自定义的派生类
                        typeInfo.PolymorphismOptions.DerivedTypes.Add(
                            new JsonDerivedType(typeof(ExtendedSubData), "ExtendedSubData"));
                        // 设置类型鉴别器的属性名,默认是$type
                        typeInfo.PolymorphismOptions.TypeDiscriminatorPropertyName = "type";
                    }
                }
            }
        }
    };

    var serialized = JsonSerializer.Serialize(data, options);
}

该方案适合用户能够控制序列化配置的场景,无需自定义转换器,利用框架内置的多态支持。

方案三:修复序列化泛型类型错误

原代码中使用JsonSerializer.Serialize<object>(data)会导致序列化时丢失具体类型信息,应直接序列化Data类型:

void SerializData(Data data)
{
    // 配合方案一或方案二的options使用
    var options = new JsonSerializerOptions();
    // 添加转换器或多态配置...
    var serialized = JsonSerializer.Serialize(data, options);
}

方案选择建议

  • 如果需要服务端反序列化子类对象,方案一更灵活,无需提前注册所有派生类,但需要处理类型字符串解析。
  • 如果用户可以自主配置序列化选项,方案二更简洁,符合System.Text.Json的原生多态设计。
  • 无论采用哪种方案,都要避免将序列化泛型类型指定为object,否则会丢失类型元数据。

内容的提问来源于stack exchange,提问作者Ivan Pavić

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 03:45:07