首页 > 解决方案 > C# - 发布具有通用属性的模型(Web API)

问题描述

我有这种情况:

public class Dto 
{
    public int TypeId { get; set; }
    public IType Type { get; set; }
}

public class Type1 : IType
{
   public string PropertyA { get; set; }
}

public class Type2 : IType
{
   public int PropertyB { get; set; }
   public bool PropertyC { get; set; }
}

public class MyController : ApiController
{
   [HttpPost]
   public IHttpActionResult Post(Dto dto) 
   {
   }
}

如何根据 TypeId 属性的值反序列化 IType 接口的正确实现?

我尝试使用 JsonConverter(以下示例:https ://gist.github.com/teamaton/bba69cf95b9e6766f231 ),但我只能指定一种具体类型:

public class Dto 
{
    public int TypeId { get; set; }

    [JsonConverter(typeof(ConcreteTypeConverter<Type1>)]
    public IType Type { get; set; }
}

标签: c#.netapimodel-view-controller

解决方案


JsonConverter是正确的方法,但ConcreteTypeConverter不适合您的情况。

假设您需要根据 确定在运行时创建的具体类型TypeId,您将需要一个JsonConverteron Dtonot onType属性。

尝试这个:

[JsonConverter(typeof(DtoJsonConverter))]
public class Dto
{
    public IType Type { get; set; }
    public int TypeId { get; set; }
}

class DtoJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Dto);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        // Load this to a JObject so that we can read TypeId
        var obj = JObject.Load(reader);
        var typeId = obj["TypeId"].Value<int>();

        // Figure out JSON covnerter for type property based on TypeId
        JsonConverter converter = null;
        switch (typeId)
        {
            // Assuming 1 means Type1
            case 1:
                converter = new CreateITypeJsonConverter(() => new Type1());
                break;
            case 2:
                converter = new CreateITypeJsonConverter(() => new Type2());
                break;
        }

        if (converter != null)
        {
            serializer.Converters.Add(converter);
        }

        try
        {
            // Now create Dto and populate the object.
            // This will call the JsonConverter we just added for Type property.
            var dto = new Dto();
            serializer.Populate(obj.CreateReader(), dto);
            return dto;
        }
        finally
        {
            if (converter != null)
            {
                serializer.Converters.Remove(converter);
            }
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
}

class CreateITypeJsonConverter : CustomCreationConverter<IType>
{
    private readonly Func<IType> _factory;

    public CreateITypeJsonConverter(Func<IType> factory)
    {
        _factory = factory;
    }

    public override IType Create(Type objectType)
    {
        return _factory();
    }
}

根据的值计算DtoJsonConverter出具体类型,并使用另一个实例化具体类型,然后填充.ITypeTypeIdCreateITypeJsonConverterDto

您也可以TypeId进入IType,然后使用此问题中的一种方法:JsonConverter with Interface


推荐阅读