Десериализирайте 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