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

How to bind a Polymorphic Properties of a Models in .NET core

我有一个 ASP.NET Core Web API 端点,它采用 (FromBody) 下面定义的搜索对象

public class Search {

public int PageSize {get;set;}
public Expression Query{get;set;}
}

Public class Expression {
  public string Type {get;set;}
}

public class AndExpression {
  public IList<Expression> Expressions {get;set;}
}

public class MatchesExpression {
  public string FieldId {get;set;}
  public string Value {get;set;}
  public string Operator {get;set;}
}

所以...如果我 post 以下 JSON 到我的端点

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

我成功获得了搜索对象,但查询 属性 是表达式类型,而不是 MatchesExpression。

这显然是一个多态问题。

这篇文章(接近尾声)给出了一个很好的例子,说明当您的整个模型都是多态时如何处理这个问题。

https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-5.0

在我的例子中,我的模型“查询”的 属性 是多态的,所以我不确定如何为我的搜索对象构建一个 ModelBinder 来处理查询 属性

我想象一下,我需要编写一个模型绑定器来构建搜索对象,然后遵循为 属性 描述的模式,但是我找不到任何示例来说明如何实现一个不完整的模型绑定器琐碎。

关于如何实现这一点有什么建议吗?良好的信息来源?

所以..我放弃了 ModelBInders(因为我使用的 FromBody 属性与我的目标不兼容)。

相反,我写了一个 System.Text.Json JsonConvertor 来处理多态性(见下面的 shonky 代码)

using Searchy.Models;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace Searchy
{

    public class ExpressionJsonConverter : JsonConverter<Expression>
    {
        public override Expression Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {

            Utf8JsonReader readerClone = reader;

            using (var jsonDocument = JsonDocument.ParseValue(ref readerClone)) 
            { 
                if (!jsonDocument.RootElement.TryGetProperty("type", out var typeProperty))
                 {
                 throw new JsonException();
                }

                switch (typeProperty.GetString()) 
                {
                    case "comparison":
                        return JsonSerializer.Deserialize<Comparison>(ref reader, options);
                    case "and":
                        return JsonSerializer.Deserialize<And>(ref reader, options);
                }
            }

            return null;
        }

        public override void Write(
            Utf8JsonWriter writer,
            Expression expression,
            JsonSerializerOptions options)
        {

        }
    }
}

我的表情class还有以下属性

[JsonConverter(typeof(ExpressionJsonConverter))]