Десериализовать интерфейс в теле запроса на основе конкретного условия ядра asp.net - PullRequest
0 голосов
/ 06 мая 2019

Я создаю небольшое веб-приложение, используя ASP.Net Core 2.2.

В настоящее время у меня один интерфейс:

public interface ILocation {

    public int Type {get;}
}

Есть 2 класса, реализующих этот ILocation интерфейс:


public class StreetLocation: ILocation {

    public int Type {get;set;}

    public string City {get;set;}

    public string State {get;set;}
}

public class GeoCoordinateLocation: ILocation {

    public int Type {get;set;}

    public double Latitude{get;set;}

    public double Longitude{get;set;}
}

У меня есть один класс StudentViewModel, который предназначен для получения информации, отправляемой от клиента к серверу. Этот класс реализован следующим образом:

public class StudentViewModel {

    public Guid Id {get;set;}

    public string Name {get;set;}

    public ILocation Address {get;set;}
}

Мой запрос от клиента будет выглядеть так:

{
    "id": "0da28089-c0da-41a7-a47f-89b54d52822b",
    "name": "Student 01",
    "location": {
        "type": 1,
        ...
    }
}

Если тип местоположения равен 1, ILocation будет десериализован как StreetLocation и как GeoCoordinateLocation, если тип местоположения равен 2.

Есть какие-нибудь решения по этому поводу, пожалуйста? Может ли это быть сделано IModelBinder в ядре asp.net?

Спасибо

1 Ответ

1 голос
/ 06 мая 2019

Вы можете создать пользовательский JsonConverter для этого.

  1. создать вспомогательную функцию для разрешения типа местоположения во время выполнения: Type ResolveILocationTypeRuntime(typeCode)
  2. создать вспомогательную функцию для создания экземпляра определенного типа во время выполнения: DeserializeLocationRuntime(json, type)
  3. Чтение Json или Запись Json в соответствии с текущим type.

Реализация:

public class CustomLocationConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var json= JToken.ReadFrom(reader);
        var typeToken = json.SelectToken("type"); // or ??json.SelectToken("Type");
        if(typeToken ==null) return null;         //  
        var type= ResolveILocationTypeRuntime((int)typeToken);
        var location = DeserializeLocationRuntime(json, type);
        return location;
    }

    private Type ResolveILocationTypeRuntime(int type)
    {
        switch (type)
        {
            case 1:
                return typeof(StreetLocation);
            case 2:
                return typeof(GeoCoordinateLocation);
            default:
                throw new ArgumentOutOfRangeException("type should be 1|2");
        }
    }
    private ILocation DeserializeLocationRuntime(JToken json, Type locationType)
    {
        MethodInfo mi = typeof(JToken)
            .GetMethods(BindingFlags.Public | BindingFlags.Instance)
            .Where(m => m.Name == "ToObject" && m.GetParameters().Length == 0 && m.IsGenericMethod)
            .FirstOrDefault()
            ?.MakeGenericMethod(locationType);
        var location = mi?.Invoke(json, null);
        return (ILocation)location;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var location = value as ILocation;
        var type = ResolveILocationTypeRuntime(location.Type);
        serializer.Serialize(writer, location, type );
    }
}

И украшение вашего Address свойства с помощьюJsonConverterAttribute:

public class StudentViewModel
{
    public Guid Id { get; set; }
    public string Name { get; set; }

    [JsonProperty("location")]
    [JsonConverter(typeof(CustomLocationConverter))]

    public ILocation Address { get; set; }
}

Контрольный пример:

действие контроллера :

public IActionResult Test([FromBody]StudentViewModel model)
{
    return Json(model);
}

запрос:

POST https://localhost:5001/home/test HTTP/1.1
Content-Type: application/json

{
    "id": "0da28089-c0da-41a7-a47f-89b54d52822b",
    "name": "Student 01",
    "location": {
        "type":2,
        "latitude": 1.2,
        "longitude":3.1415926,
    }
}

demo

...