Десериализовать JSON с переменным именем свойства и вложенным списком в объект

У меня есть куча API, которые генерируют следующие 2 вида ответа в теле ответа:

{ "error": null, "object_type": { /* some object */ } }
{ "error": null, "object_type": [{ /* some object */ }, { /* some object */ }, ...] }

Хотя у меня есть класс, соответствующий структуре объекта, я хочу десериализовать конечные точки API непосредственно либо в объект класса, либо в List<class>, без создания некоторых классов «результатов», соответствующих структуре ответа JSON. Это возможно?

Например есть 2 API:

/api/getAllCompanies

возвращается

{ "error": null, "company": [ { "name": "Microsoft", "country": "US" }, { "name": "Apple", "country": "US" } ]

пока

/api/getUserCompany

возвращается

{ "error": null, "company": { "name": "Microsoft", "country": "US" } }

У меня есть класс в коде:

public class Company {
    string Name { get; set; }
    string Country { get; set; }
}

Как я могу напрямую десериализовать данные в объект Company или List<Company>, не создавая кучу другого класса?

(Имя свойства JSON (company) известно, поэтому его не нужно извлекать в другом месте.)

Я пытался сначала десериализовать ответ JSON в ExpandoObject, а затем скопировать свойства в экземпляр целевого класса, используя код здесь затем преобразуйте его, используя следующий код, но, похоже, это не работает со списками.

private static async Task<T> GetApiObject<T>(string api, string extractedObjName) where T: class, new()
    {
        var retstr = await /* get API response as string */;
        dynamic retobj = JsonConvert.DeserializeObject<ExpandoObject>(retstr, new ExpandoObjectConverter());
        var ret = new T();
        Mapper<T>.Map((ExpandoObject)((IDictionary<string, object>)retobj)[extractedObjName], ret);
        return ret;
    }

person Jamesits    schedule 09.01.2018    source источник
comment
Похоже на дубликат Как обрабатывать один элемент и массив для одного и того же свойства с помощью JSON.net. Но просто для ясности: известно ли имя свойства "company" заранее, поэтому десериализация в тип class Result { public string error { get; set; } public List<Company> company { get; set; } } вам нужна? Потому что SingleOrArrayConverter<Company> выполнит эту работу.   -  person dbc    schedule 09.01.2018
comment
@dbc есть и другие API, возвращающие вещи, отличные от компании; Я не хочу создавать тип результата для каждого API.   -  person Jamesits    schedule 09.01.2018


Ответы (1)


Вы можете использовать JObejct для извлечения необходимой информации, прежде чем десериализовать ее в объект.

var str = "{ \"error\": null, \"company\": [{ \"name\": \"Microsoft\", \"country\": \"US\" } ,{ \"name\": \"Apple\", \"country\": \"US\" } ]}";
var temp = JObject.Parse(str).GetValue("company");
var companies = temp.Select(x => x.ToObject<Company>()).ToList();

То же самое касается /api/getUserCompany

var str = "{ \"error\": null, \"company\": { \"name\": \"Microsoft\", \"country\": \"US\" } }";
var temp = JObject.Parse(str).GetValue("company");
var company = temp.ToObject<Company>();
person Rawitas Krungkaew    schedule 09.01.2018