ASP.NET Core Web API中POST请求多态JSON反序列化问题
ASP.NET Core Web API 多态模型绑定子类属性丢失问题解决
问题场景
使用C#开发ASP.NET Core Web API时,定义了包含基类Animal和子类Dog、Cat的多态模型,POST接口接收MyAnimals对象时,传入包含子类属性的JSON数据,控制器仅能获取基类属性,子类属性丢失。
模型类代码
public class MyAnimals { public string Description { get; set; } public ICollection<Animal> Animals { get; set; } } public class Animal { public string Name { get; set; } } public class Dog : Animal { public string Says { get; set; } } public class Cat : Animal { public string Likes { get; set; } }
Web API POST处理方法
[HttpPost] [AllowAnonymous] public IActionResult PostAnimal([FromBody] MyAnimals myAnimals) { return Ok(); }
期望请求JSON
{ "description": "My favorite animals", "animals": [ { "likes": "Cheese", "name": "Tom" }, { "says": "Bark", "name": "Pluto" } ] }
实际解析结果(子类属性丢失)
{ "description": "My favorite animals", "animals": [ { "name": "Tom" }, { "name": "Pluto" } ] }
解决方案
问题根源是默认的System.Text.Json序列化器无法自动识别多态类型,需要显式配置多态支持或自定义类型转换逻辑。
方案一:使用内置多态特性(需客户端配合)
通过JsonDerivedType特性为基类指定子类映射,序列化器会根据类型鉴别符识别子类。
- 修改基类
Animal,添加多态特性:
using System.Text.Json.Serialization; [JsonDerivedType(typeof(Dog), typeDiscriminator: "Dog")] [JsonDerivedType(typeof(Cat), typeDiscriminator: "Cat")] public class Animal { public string Name { get; set; } }
- 客户端请求时,需在每个动物对象中添加类型鉴别符
$type:
{ "description": "My favorite animals", "animals": [ { "$type": "Cat", "likes": "Cheese", "name": "Tom" }, { "$type": "Dog", "says": "Bark", "name": "Pluto" } ] }
方案二:自定义JsonConverter(无需客户端修改)
编写自定义转换器,根据JSON中的属性自动判断并实例化对应子类,无需客户端额外传递类型标识。
- 创建
AnimalConverter类:
using System.Text.Json; using System.Text.Json.Serialization; public class AnimalConverter : JsonConverter<Animal> { public override Animal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using JsonDocument doc = JsonDocument.ParseValue(ref reader); var rootElement = doc.RootElement; // 根据属性判断子类类型 if (rootElement.TryGetProperty("says", out _)) { return JsonSerializer.Deserialize<Dog>(rootElement.GetRawText(), options); } else if (rootElement.TryGetProperty("likes", out _)) { return JsonSerializer.Deserialize<Cat>(rootElement.GetRawText(), options); } // 默认返回基类实例 return JsonSerializer.Deserialize<Animal>(rootElement.GetRawText(), options); } public override void Write(Utf8JsonWriter writer, Animal value, JsonSerializerOptions options) { // 序列化时按实际类型输出 JsonSerializer.Serialize(writer, value, value.GetType(), options); } }
- 注册转换器(两种方式二选一):
- 方式一:在基类上添加特性
[JsonConverter(typeof(AnimalConverter))] public class Animal { public string Name { get; set; } }
- 方式二:在全局配置中添加转换器(.NET 6+ Program.cs)
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new AnimalConverter()); });
配置完成后,客户端直接发送原始的期望JSON即可,控制器能正确解析子类属性。
内容的提问来源于stack exchange,提问作者Pratyush Singh
相关产品推荐
相关产品推荐

