ASP.NET Core中不使用Newtonsoft的多态复杂对象反序列化问题
多态复杂对象在ASP.NET中基于System.Text.Json的反序列化问题
背景
- ASP.NET中多态复杂对象的反序列化是常见技术场景,常规方案依赖
JsonConverter或JsonSubTypes - 需求限制:禁止使用Newtonsoft.Json,必须基于
System.Text.Json和Microsoft.AspNetCore.Mvc.ModelBinding实现,原因是现有类大量使用Newtonsoft特性,但这些特性未针对ASP.NET反序列化做优化
问题现象
按照微软官方文档的ModelBinder属性方案,已实现多态对象的基础反序列化,但无法处理包含非多态对象集合的复杂多态对象:例如抽象类Vehicle的RepairHistory(RepairRecord数组类型)属性,在Create接口方法中反序列化后丢失。
问题根源
自定义多态ModelBinder仅处理了多态类型的实例创建逻辑,未触发默认ModelBinding机制去递归填充实例的非多态属性(尤其是集合类型属性)。当自定义Binder创建完多态实例后直接返回成功结果,导致剩余属性(如RepairHistory)未被绑定。
解决方案
修改自定义ModelBinder的BindModelAsync方法,在创建多态实例后,将实例赋值给ModelBindingContext.Model,再调用TryUpdateModelAsync让默认绑定器处理所有剩余属性的绑定:
public class VehicleModelBinder : IModelBinder { public async Task BindModelAsync(ModelBindingContext bindingContext) { // 1. 获取区分多态类型的标识 var vehicleTypeValue = bindingContext.ValueProvider.GetValue("VehicleType").FirstValue; if (string.IsNullOrEmpty(vehicleTypeValue)) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "VehicleType is required."); return; } // 2. 创建对应多态实例 Vehicle vehicle = vehicleTypeValue switch { "Car" => new Car(), "Truck" => new Truck(), _ => throw new NotSupportedException($"Vehicle type {vehicleTypeValue} is not supported.") }; // 3. 触发默认绑定逻辑填充剩余属性(包括集合) bindingContext.Model = vehicle; await bindingContext.TryUpdateModelAsync(vehicle, "", null, null); if (!bindingContext.ModelState.IsValid) { bindingContext.Result = ModelBindingResult.Failed(); return; } bindingContext.Result = ModelBindingResult.Success(vehicle); } }
同时确保多态类的ModelBinder属性正确标记:
[ModelBinder(BinderType = typeof(VehicleModelBinder))] public abstract class Vehicle { public string VehicleType { get; set; } public RepairRecord[] RepairHistory { get; set; } } public class Car : Vehicle { /* 子类自定义属性 */ } public class Truck : Vehicle { /* 子类自定义属性 */ } public class RepairRecord { public DateTime Date { get; set; } public string Description { get; set; } }
Controller接口方法正常接收参数即可:
[HttpPost] public IActionResult Create([FromBody] Vehicle vehicle) { // 此时vehicle.RepairHistory已完成正确绑定 return Ok(vehicle); }
注意:若为JSON请求,需确保
System.Text.Json序列化选项配置了正确的属性映射规则;若为表单提交,需保证请求参数命名与类属性一致。
内容的提问来源于stack exchange,提问作者T. Jastrzębski
相关产品推荐
相关产品推荐

