.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
相关产品推荐
相关产品推荐

