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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 07:48:32