Десериализовать объект с помощью IEnumerable с использованием JSON.NET - PullRequest
0 голосов
/ 05 сентября 2018

Рассмотрим следующие json и модели:

{
    "Reference": "Bay House 22",
    "Appliances": [
        {
            "Reference": "Kitchen Appliance 1",
            "ApplianceType": "0",
            "NumberOfSlots": 4
        },
        {
            "Reference": "Kitchen Appliance 2",
            "ApplianceType": "1",
            "Capacity": 1500
        }
    ]
}

public class HouseModel
{
    public String Reference { get; set; }
    [JsonConverter(typeof(ApplianceModelConverter))]
    public IEnumerable<IApplianceModel> Appliances { get; set; }
}

public interface IApplianceModel
{
    String Reference { get; set; }
    ApplianceType ApplianceType { get; set; } // this is an enum
}

public class ToasterModel : IApplianceModel
{
    public String Reference { get; set; }
    public ApplianceType ApplianceType { get; set; }

    public Int32 NumberOfSlots { get; set; }
}

public class KettleModel : IApplianceModel
{
    public String Reference { get; set; }
    public ApplianceType ApplianceType { get; set; }

    public Int32 Capacity { get; set; }
}

Я пытаюсь десериализовать IEnumerable, который может быть Toaster или Kettle, используя специальный конвертер json. Идея в том, что я могу вернуть конкретный тип, как только узнаю, что такое ApplianceType, посмотрев на json. Я следил за этой публикацией stackoverflow, чтобы попытаться заставить это работать, но безуспешно.

Вот код конвертера:

public abstract class JsonCreationConverter<T> : JsonConverter
{
    protected abstract T Create(Type objectType, JObject jObject);

    public override Boolean CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override Boolean CanWrite
    {
        get { return false; }
    }

    public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
    {
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
}

public class ApplianceModelConverter: JsonCreationConverter<IApplianceModel>
{
    protected override IApplianceModel Create(Type objectType, JObject jObject)
    {
        if (jObject["applianceType "] == null)
        {
            throw new ArgumentException("Missing ApplianceType");
        }

        ApplianceType applianceType = jObject.Value<ApplianceType>();

        switch (applianceType )
        {
            case ApplianceType.Kettle:
                return new KettleModel();
            case ApplianceType.Toaster: 
                return new ToasterModel();
            default:
                throw new InvalidEnumArgumentException("ApplianceType not supported");
        }
    }
}

В настоящее время я получаю исключение при выполнении этой строки: JObject jObject = JObject.Load(reader); в JsonCreationConverter

Newtonsoft.Json.JsonReaderException: 'Ошибка чтения JObject из JsonReader. Текущий элемент JsonReader не является объектом: StartArray. Дорожка 'Техника'

Полагаю, что он достигает IEnumerable и просто не работает, что я тут не так делаю?

1 Ответ

0 голосов
/ 05 сентября 2018

Прокомментируйте эту строку кода

    public class HouseModel
{
    public String Reference { get; set; }
    //[JsonConverter(typeof(ApplianceModelConverter))] //=> You don't need this
    public IEnumerable<IApplianceModel> Appliances { get; set; }
}

Также измените метод Create на этот

 protected override IApplianceModel Create(Type objectType, JObject jObject)
    {
        if (jObject["ApplianceType"] == null) //case sensitive
        {
            throw new ArgumentException("Missing ApplianceType");
        }

        //ApplianceType applianceType = jObject.Value<ApplianceType>(); //this might throw invalid cast exception
        ApplianceType applianceType = jObject["ApplianceType"].ToObject<ApplianceType>();


        switch (applianceType)
        {
            case ApplianceType.Kettle:
                return new KettleModel();
            case ApplianceType.Toaster:
                return new ToasterModel();
            default:
                throw new InvalidEnumArgumentException("ApplianceType not supported");
        }
    }

Если мы используем этот метод для десериализации

string json = "{\"Reference\": \"Bay House 22\",\"Appliances\": [{\"Reference\": \"Kitchen Appliance 1\",\"ApplianceType\": \"0\",\"NumberOfSlots\": 4},{\"Reference\": \"Kitchen Appliance 2\",\"ApplianceType\": \"1\",\"Capacity\": 1500}]}";

HouseModel houseModels = JsonConvert.DeserializeObject<HouseModel>(json, new ApplianceModelConverter());
...