1

I have a bunch of API which generates the following 2 kinds of reply in response body:

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

While I have a class corresponding to the object structure, I want to deserialize the API endpoints directly into either an object of the class or a List<class>, without creating some "result" classes to match the response JSON structure. Is this possible?

For example there is 2 API:

/api/getAllCompanies

returns

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

while

/api/getUserCompany

returns

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

I have a class in code:

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

How can I directly deserialize the data into a Company object or a List<Company> without creating a bunch of other class?

(The JSON property name (company) is known so don't need to extract it elsewhere.)

I've been trying to first deserialize the response JSON into an ExpandoObject then copy the properties to an instance of destination class using the code here then convert it using the following code, but this seems not to work with lists.

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;
    }
Jamesits
  • 612
  • 7
  • 18
  • 1
    Looks like a duplicate of [How to handle both a single item and an array for the same property using JSON.net](https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n). But just to be clear -- is the property name `"company"` known in advance, so deserializing to a type `class Result { public string error { get; set; } public List company { get; set; } }` what you need? Because `SingleOrArrayConverter` will do the job. – dbc Jan 09 '18 at 07:02
  • @dbc there are other APIs returning things other than Company; I don't want to create a result type for every API. – Jamesits Jan 09 '18 at 07:19

1 Answers1

1

You can use JObejct to extract the information you need before deserialize it into the object.

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();

Same goes for /api/getUserCompany

var str = "{ \"error\": null, \"company\": { \"name\": \"Microsoft\", \"country\": \"US\" } }";
var temp = JObject.Parse(str).GetValue("company");
var company = temp.ToObject<Company>();
Ray Krungkaew
  • 6,652
  • 1
  • 17
  • 28