.NET Core控制器如何实现子类对象的JSON自动绑定?
解决ASP.NET Core中多态类型的JSON反序列化问题
这个问题本质是JSON反序列化的多态支持——默认情况下,ASP.NET Core用的System.Text.Json只会把请求体转换成你在Action参数里指定的基类(也就是Person),子类独有的属性(比如Doctor的specialization)会直接被忽略。不过不用一个个写自定义绑定,通过全局配置就能一次性解决上百个派生类的问题,下面分两种常用场景给你讲:
一、用默认的System.Text.Json(ASP.NET Core 3.0+)
1. 全局配置多态支持
在你的Program.cs(.NET 6+)或者Startup.cs(.NET 5及更早)里,给控制器的JSON序列化选项开启多态,并且通过反射自动扫描所有Person的派生类,不用手动逐个添加:
.NET 7+ 简洁版
using System.Reflection; using System.Text.Json.Serialization; // Program.cs 里的配置 builder.Services.AddControllers() .AddJsonOptions(options => { // 给Person类开启多态支持,自动注册所有派生类 options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, JsonSerializerOptions.Default.WithAddedPolymorphism<Person>( typeof(Person).Assembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(Person))) .Select(t => (Type)t) )); });
.NET 6 兼容版
如果你的项目是.NET 6,因为没有WithAddedPolymorphism扩展方法,可以用下面的代码手动配置:
using System.Reflection; using System.Text.Json.Serialization; builder.Services.AddControllers() .AddJsonOptions(options => { // 扫描当前程序集里所有Person的非抽象子类 var derivedPersonTypes = typeof(Person).Assembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(Person))) .Select(t => new JsonDerivedType(t, t.Name)); // 用类名作为类型标识 // 给Person类型添加多态配置 var personTypeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeof(Person)) as JsonTypeInfo<Person>; foreach (var type in derivedPersonTypes) { personTypeInfo.PolymorphismOptions.DerivedTypes.Add(type); } // 指定用来标识类型的字段名(默认是$type) personTypeInfo.PolymorphismOptions.TypeDiscriminatorPropertyName = "$type"; });
2. 发送带类型标识的JSON
现在你只需要在请求的JSON里加一个$type字段,告诉序列化器要转换成哪个子类就行:
{ "$type": "Doctor", "specialization": "obgyn" }
二、用Newtonsoft.Json(Json.NET)
如果你的项目习惯用Newtonsoft.Json(可以替换默认的序列化器),配置会更简单:
1. 替换序列化器并开启多态
builder.Services.AddControllers() .AddNewtonsoftJson(options => { // 自动为对象添加类型信息 options.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto; // 如果你想更严谨,也可以用TypeNameHandling.Objects,只给对象加类型标识 });
2. 发送带类型信息的JSON
Newtonsoft默认用$type字段,不过需要填写完整的类名和程序集名称,比如:
{ "$type": "YourProject.Models.Doctor, YourProject", "specialization": "obgyn" }
这里的YourProject.Models.Doctor是你的Doctor类的完整命名空间,YourProject是程序集名称(一般就是项目名)。
为啥不用自定义绑定?
自定义模型绑定确实能实现,但你有上百个派生类的话,每个类都要写绑定逻辑,工作量超大。而通过全局配置JSON的多态支持,是一次性解决所有派生类问题的最优方案。
内容的提问来源于stack exchange,提问作者Kristijan Bilić
相关产品推荐
相关产品推荐

