基于特定条件的asp.net核心在请求正文中反序列化接口

时间:2019-05-06 03:42:18

标签: c# asp.net-core asp.net-core-webapi asp.net-core-2.1

我正在使用ASP.Net Core 2.2制作一个小型Web应用程序。

当前我有一个界面:

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 个答案:

答案 0 :(得分:1)

您可以创建一个自定义JsonConverter来做到这一点。

  1. 创建一个辅助函数以在运行时解析位置类型:Type ResolveILocationTypeRuntime(typeCode)
  2. 创建一个辅助函数,以在运行时创建某个特定类型的实例:DeserializeLocationRuntime(json, type)
  3. 根据当前type读取Json或写Json。

实施:

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