【问题标题】:C# - Post a model with generic property (Web API)C# - 发布具有通用属性的模型(Web API)
【发布时间】:2020-07-03 11:03:32
【问题描述】:

我有这种情况:

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# .net api model-view-controller


    【解决方案1】:

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

    假设您需要在运行时根据TypeId 确定要创建的具体类型,您将需要JsonConverterDto 而不是Type 属性上。

    试试这个:

    [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根据TypeId的值计算出IType的具体类型,并使用另一个CreateITypeJsonConverter实例化具体类型,然后填充Dto

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-14
      • 1970-01-01
      • 2011-01-03
      • 1970-01-01
      • 2020-04-08
      相关资源
      最近更新 更多