C# - публикация модели со свойством generi c (веб-API) - PullRequest
0 голосов
/ 23 марта 2020

У меня такая ситуация:

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

Как я могу десериализовать правильную реализацию интерфейса IType, в зависимости от значения свойства TypeId?

Я пытался использовать JsonConverter (следующее этот пример: https://gist.github.com/teamaton/bba69cf95b9e6766f231), но я могу указать только один конкретный тип:

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

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

1 Ответ

1 голос
/ 23 марта 2020

JsonConverter - правильный путь к go, однако ConcreteTypeConverter не подходит для вашего случая.

Предположим, вам нужно определить, какой конкретный тип создать во время выполнения, на основе TypeId , вам понадобится JsonConverter на Dto, а не на 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 определяет конкретный тип IType согласно значению TypeId, и использовать другой CreateITypeJsonConverter для создания экземпляра конкретного типа, затем заполните Dto.

Также возможно, что вы могли бы переместить TypeId в IType, затем используйте один из методов в этом вопросе: JsonConverter с интерфейсом

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...