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

Time:10-15

I have an ASP.NET Core Web API end point which takes (FromBody) The Search object defined below

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;}
}

So... if I post the following JSON to my endpoint

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

I successfully get a Search Object, but the Query property is of type Expression, not MatchesExpression.

This is clearly a polymorphic issue.

This article (towards the end) gives a good example of a how to deal with this issue when your entire model is polymorphic.

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

In my case, the property of my Model "Query" is polymorphic, so Im unsure how to build a ModelBinder for my Search object that will allow me to handle the Query Property

I Imagine, I need to write a model binder to construct the search object and then follow the pattern described for the property, however I cannot locate any examples of how to implement a model binder that isnt utterly trivial.

Any suggestions on how to achieve this? Good sources of information?

CodePudding user response:

So.. I gave up with ModelBInders (because Im using the FromBody attribute which isnt compatible with my aims).

Instead I wrote a System.Text.Json JsonConvertor to handle the polymorphism (see shonky code below)

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)
        {

        }
    }
}

My Expression class also had the following attribue

[JsonConverter(typeof(ExpressionJsonConverter))]
  • Related