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

.NET 9中如何自动按约定将所有派生类型加入JsonDerivedType列表

自动注册派生类实现System.Text.Json多态序列化/反序列化

问题背景

我目前用JsonDerivedType属性实现了基类的派生类型序列化/反序列化,功能正常,但每次新增派生类都要修改基类上的属性配置,比如:

[JsonDerivedType(typeof(MyConfigDerived), typeDiscriminator: "derived")]
[JsonDerivedType(typeof(MyConfigAlternate), typeDiscriminator: "alternate")]
[JsonDerivedType(typeof(MyConfigAnother), typeDiscriminator: "another")]
public class MyConfigBase { ... }

我希望能在运行时自动发现所有派生类,不用每次修改基类。比如基类只保留自身的判别器配置:

[JsonDerivedType(typeof(MyConfigBase), typeDiscriminator: "base")]
public class MyConfigBase { ... }

派生类只需直接声明继承关系即可:

public class MyConfigDerived : MyConfigBase { ... }
public class MyConfigAlternate : MyConfigBase { ... }
public class MyConfigAnother : MyConfigBase { ... }

核心需求是让合作者能新增派生类而无需修改基类,降低扩展成本。

实现方案

通过扫描程序集查找所有派生类,手动向JsonSerializerOptions添加多态配置,完全摆脱对JsonDerivedType属性的依赖。

1. 派生类扫描与配置工具

using System.Reflection;
using System.Text.Json.Serialization;

public static class PolymorphicTypeHelper
{
    /// <summary>
    /// 扫描所有加载的程序集,找到继承自指定基类的具体实现类
    /// </summary>
    public static IEnumerable<Type> FindDerivedTypes<TBase>()
    {
        var baseType = typeof(TBase);
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();

        foreach (var assembly in assemblies)
        {
            foreach (var type in assembly.GetTypes())
            {
                // 排除基类本身、抽象类、接口,只保留可实例化的派生类
                if (type != baseType && baseType.IsAssignableFrom(type) 
                    && !type.IsAbstract && !type.IsInterface)
                {
                    yield return type;
                }
            }
        }
    }

    /// <summary>
    /// 为JsonSerializerOptions批量添加基类的所有派生类多态配置
    /// </summary>
    public static void AddPolymorphicTypes<TBase>(this JsonSerializerOptions options, Func<Type, string> discriminatorGenerator)
    {
        var baseType = typeof(TBase);
        var typeInfoResolver = options.TypeInfoResolver as DefaultJsonTypeInfoResolver 
                              ?? new DefaultJsonTypeInfoResolver();

        // 修改基类的JsonTypeInfo,注入派生类配置
        typeInfoResolver.Modifiers.Add(typeInfo =>
        {
            if (typeInfo.Type != baseType) return;

            typeInfo.PolymorphismOptions ??= new JsonPolymorphismOptions();
            
            // 确保基类自身的判别器已配置(如果未通过属性声明)
            if (!typeInfo.PolymorphismOptions.DerivedTypes.Any(dt => dt.DerivedType == baseType))
            {
                typeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(baseType, discriminatorGenerator(baseType)));
            }

            // 批量添加所有派生类
            foreach (var derivedType in FindDerivedTypes<TBase>())
            {
                if (!typeInfo.PolymorphismOptions.DerivedTypes.Any(dt => dt.DerivedType == derivedType))
                {
                    typeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derivedType, discriminatorGenerator(derivedType)));
                }
            }
        });

        options.TypeInfoResolver = typeInfoResolver;
    }
}

2. 配置Json序列化选项

在应用启动时(比如ASP.NET Core的Program.cs)配置全局或局部序列化选项:

// 局部选项配置
var jsonOptions = new JsonSerializerOptions();
// 自定义判别器生成规则:去掉类名前缀并转小写(MyConfigDerived → derived)
jsonOptions.AddPolymorphicTypes<MyConfigBase>(type => 
    type.Name.Replace("MyConfig", string.Empty).ToLowerInvariant());

// ASP.NET Core全局配置
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.AddPolymorphicTypes<MyConfigBase>(type => 
            type.Name.Replace("MyConfig", string.Empty).ToLowerInvariant());
    });

3. 测试示例

// 序列化派生类实例
var derivedConfig = new MyConfigDerived { /* 赋值属性 */ };
string json = JsonSerializer.Serialize<MyConfigBase>(derivedConfig, jsonOptions);
// 输出示例:{"$type":"derived", "Property1": "..."}

// 反序列化回对应类型
var deserialized = JsonSerializer.Deserialize<MyConfigBase>(json, jsonOptions);
Console.WriteLine(deserialized is MyConfigDerived); // 输出True

关键说明

  • 程序集扫描:默认扫描当前AppDomain加载的所有程序集,可根据需求限制扫描范围(比如只包含特定命名空间的程序集)
  • 判别器规则:可自定义生成逻辑,比如使用类的FullName、自定义特性值,或其他业务规则
  • 重复配置防护:代码中添加了重复检查,避免多次添加同一派生类的配置
  • 版本兼容:基于DefaultJsonTypeInfoResolver实现,支持.NET 6及以上版本的System.Text.Json

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 08:47:37