deadManN deadManN - 5 months ago 80
JSON Question

HttpClient parse JSON object to List, instead of JSON Array

One of our customer require some data, which should be obtained from a service provider. the service is written in PHP, and is more like a web api than a soap or wcf service, and the response is in following format:


{"0":{"Code":"AL","Name":"ALBANIA"},"1":{"Code":"DZ","Name":"ALGERIA"},"2":{"Code":"AD","Name":"ANDORRA"},"3":{"Code":"AO","Name":"ANGOLA"},"4":{"Code":"AI","Name":"ANGUILLA"},"5":{"Code":"AG","Name":"ANTIGUA"},"6":{"Code":"AR","Name":"ARGENTINA"},"7":{"Code":"AM","Name":"ARMENIA"},"8":{"Code":"AW","Name":"ARUBA"},"9":{"Code":"AU","Name":"AUSTRALIA"},"10":{"Code":"AT","Name":"AUSTRIA"},"11":{"Code":"AZ","Name":"AZERBAIJAN"},"12":{"Code":"BS","Name":"BAHAMAS"},"StartTime":"2016-06-13
04:57:15","EndTime":"2016-06-13 04:57:15"}


As you can see it's an array but in a format of an object, that's what cause me issues.

I use HttpClient and my model is like this:

public class CountryVM
{
public string Code { get; set; }

public string Name { get; set; }
}


i also extend it to make it part of following model:

public class CountryResponseVM
{
public List<CountryVM> CountryVMs { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
}


when i run the following code:

using (var client = new HttpClient())
{
var response = client.PostAsync(command, new StringContent(string.Empty)).Result;
if (response.IsSuccessStatusCode)
{
List<CountryVM> readAsAsync = response.Content.ReadAsAsync<List<CountryVM>>().Result;
}
}


Either with 'CountryVM' or with 'CountryResponseVM' class, it throw following exception:


An exception of type 'System.Net.Http.UnsupportedMediaTypeException'
occurred in System.Net.Http.Formatting.dll but was not handled in user
code

Additional information: No MediaTypeFormatter is available to read an
object of type 'List`1' from content with media type 'text/html'.


How can i reformat the response, or parse json object as an array before calling
ReadAsAsync
method.




UPDATE



I also have another model, The city which is listed under countries.
The city model seem to be more right, it has array instead of object, and gave the array a name, but still i have same issue with it as i had with the countries, in all cases.

Response:


{"CityInfo":[{"CityCode":"TIA-","Name":"Albania"},{"CityCode":"TIA-7","Name":"Berat"},{"CityCode":"TIA-3","Name":"Durres"},{"CityCode":"TIA-4","Name":"Korce"},{"CityCode":"TIA-8","Name":"Pogradec"},{"CityCode":"TIA-2","Name":"Sarande"},{"CityCode":"TIA-6","Name":"Shkoder"},{"CityCode":"TIA-1","Name":"Tirana"},{"CityCode":"TIA-5","Name":"Vlore"}],"StartTime":"2016-06-13
06:03:34","EndTime":"2016-06-13 06:03:34"}


Models:

public class CityResponseVM
{
public List<CityVM> CityInfo { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
}

public class CityVM
{
public string CityCode { get; set; }
public string Name { get; set; }
}


And Request:

string command = Otrams.Url+Otrams.GetAction(ServiceAction.CityList) +"&username="+Otrams.Username+"&password="+Otrams.Password+"&gzip=no&country=AL";
using (var client = new HttpClient())
{
var response = client.PostAsync(command, new StringContent(string.Empty)).Result;
if (response.IsSuccessStatusCode)
{
//CityResponseVM readAsAsync = response.Content.ReadAsAsync<CityResponseVM>().Result;
var rawData = response.Content.ReadAsStringAsync().Result;
var myList = JsonConvert.DeserializeObject<IEnumerable<CityVM>>(rawData);
}
}

Answer

Use two objects, one for deserializing, and one to actually contain the result.

public class MyTempModel
{
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }
    [JsonExtensionData]
    public Dictionary<string, object> Countries { get; set; }
}

public class MyRealModel : Dictionary<int, CountryVM>
{
    public DateTime? StartTime { get; set; }
    public DateTime? EndTime { get; set; }
}

Deserialize:

var myList = JsonConvert.DeserializeObject<MyTempModel>(jsonResult);

var model = new MyRealModel
{
    StartTime = myList.StartTime,
    EndTime = myList.EndTime
};

foreach (var temp in myList.Countries)
{
    // Deserialize the actual ContryVm.
    var obj = JsonConvert.DeserializeObject<CountryVM>(temp.Value.ToString());
    int key = 0;
    int.TryParse(temp.Key, out key);
    model.Add(key, obj);
}

The key component here is to use JsonExtensionData, as suggested here: How to serialize a Dictionary as part of its parent object using Json.Net. It will enable the dictionary format, with extra properties such as StartTime and EndTime.

A more advanced solution would be to use a JsonConverter.

See chat for more info.