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

如何在.NET Core中绑定模型的多态属性?

实现方案

针对你遇到的[FromBody]场景下的属性多态绑定需求,优先选择JSON序列化器层面的多态配置方案,比自定义模型绑定器更简洁高效,以下是按优先级排序的实现方式:

方案1:System.Text.Json 原生多态配置(适用ASP.NET Core 7+)

.NET 7及以上版本的System.Text.Json原生支持多态序列化,只需给基类加特性配置即可:

// 为基类指定派生类和对应的类型鉴别器
[JsonDerivedType(typeof(AndExpression), typeDiscriminator: "And")]
[JsonDerivedType(typeof(MatchesExpression), typeDiscriminator: "Matches")]
public class Expression {
    // 标注为类型鉴别器字段
    [JsonDiscriminator]
    public string Type {get;set;}
}

请求传参时在query节点内增加type字段即可自动反序列化为对应子类,示例:

{ 
  "pageSize": 10, 
  "query": { 
    "type": "Matches",
    "fieldId": "body", 
    "value": "cake", 
    "operator": "matches"
  }
}

方案2:自定义JsonConverter(适用所有ASP.NET Core版本)

如果是.NET 6及更早版本,或者不想增加类型鉴别字段,可以自定义JsonConverter处理Expression类型的反序列化:

第一步:编写Converter实现

public class ExpressionJsonConverter : JsonConverter<Expression>
{
    public override Expression Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var jsonDoc = JsonDocument.ParseValue(ref reader);
        var root = jsonDoc.RootElement;

        // 方式1:根据type字段判断类型
        if (root.TryGetProperty("type", out var typeProp))
        {
            var typeValue = typeProp.GetString();
            return typeValue switch
            {
                "Matches" => JsonSerializer.Deserialize<MatchesExpression>(root.GetRawText(), options),
                "And" => JsonSerializer.Deserialize<AndExpression>(root.GetRawText(), options),
                _ => throw new NotSupportedException($"不支持的表达式类型:{typeValue}")
            };
        }

        // 方式2:根据字段特征自动判断,无需传type
        if (root.TryGetProperty("fieldId", out _) && root.TryGetProperty("operator", out _))
        {
            return JsonSerializer.Deserialize<MatchesExpression>(root.GetRawText(), options);
        }
        if (root.TryGetProperty("expressions", out _))
        {
            return JsonSerializer.Deserialize<AndExpression>(root.GetRawText(), options);
        }

        throw new InvalidOperationException("无法识别的表达式类型");
    }

    public override void Write(Utf8JsonWriter writer, Expression value, JsonSerializerOptions options)
    {
        // 序列化时保留实际子类的字段
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
}

第二步:注册Converter

在Program.cs中配置全局Json选项:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new ExpressionJsonConverter());
    });

该方案无需修改现有业务代码,也不用自定义模型绑定器,即可实现Query属性的多态反序列化。

方案3:自定义ModelBinder(仅特殊场景使用)

如果确实需要通过模型绑定器实现(比如非JSON传参场景),可以参考以下实现:

第一步:编写模型绑定器

public class SearchModelBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var request = bindingContext.HttpContext.Request;
        request.EnableBuffering();
        using var reader = new StreamReader(request.Body, leaveOpen: true);
        var bodyContent = await reader.ReadToEndAsync();
        request.Body.Position = 0;

        using var jsonDoc = JsonDocument.Parse(bodyContent);
        var root = jsonDoc.RootElement;

        var search = new Search();
        // 绑定普通属性
        if (root.TryGetProperty("pageSize", out var pageSizeProp))
        {
            search.PageSize = pageSizeProp.GetInt32();
        }
        // 绑定多态Query属性
        if (root.TryGetProperty("query", out var queryProp))
        {
            if (queryProp.TryGetProperty("fieldId", out _))
            {
                search.Query = JsonSerializer.Deserialize<MatchesExpression>(queryProp.GetRawText());
            }
            else if (queryProp.TryGetProperty("expressions", out _))
            {
                search.Query = JsonSerializer.Deserialize<AndExpression>(queryProp.GetRawText());
            }
        }

        bindingContext.Result = ModelBindingResult.Success(search);
    }
}

第二步:绑定到Search类

[ModelBinder(BinderType = typeof(SearchModelBinder))]
public class Search {
    public int PageSize {get;set;}
    public Expression Query{get;set;}
}
注意事项
  • [FromBody]的绑定逻辑默认委托给JSON序列化器处理,方案1、2是最优解,无需侵入模型绑定流程
  • 优先使用类型鉴别字段的方式判断类型,比字段特征判断更稳定,避免后续业务迭代导致的判断逻辑异常
  • 如果项目使用Newtonsoft.Json而非默认的System.Text.Json,可编写对应版本的自定义JsonConverter,逻辑完全一致

内容的提问来源于stack exchange,提问作者iasksillyquestions

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 03:15:03