1

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);
    }
}
Hassan Faghihi
  • 1,888
  • 1
  • 37
  • 55

2 Answers2

1

Grab NewtonSoft.JSON from NuGet and install it with your project. Then make the following:

Mark your CountryVM class with the [Serializable] or [DataContract] attribute (I always forget which goes where)

Then, you simply convert your Response.Content as JSON, and serialize it:

var rawData = Response.Content.ReadAll(); // forgot the proper syntax, just get the content as string

var myList = JsonConvert.DeserializeObject<IEnumerable<CountryVM>>(rawData);

"myList" now is an enumerable collection as you wanted. Please note that the syntax is probably wrong as I'm replying off the top of my head, but the procedure is valid and should solve your problem.

Pedro G. Dias
  • 3,162
  • 1
  • 18
  • 30
  • With either `CountryVM` or `CountryResponseVM` or even `CityResponseVM` or `CityVM`: Additional information: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[Otrams.Controllers.CountryVM]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. – Hassan Faghihi Jun 13 '16 at 06:01
  • Check this out: http://www.newtonsoft.com/json/help/html/SerializingCollections.htm – Pedro G. Dias Jun 13 '16 at 10:00
  • Also, make sure ALL your objects in code are [Serializable]. This is important. – Pedro G. Dias Jun 13 '16 at 10:00
  • well it did worked but through what smokednes said, and i test it, it didn't required serializable attribute, BTW thanks. – Hassan Faghihi Jun 13 '16 at 12:30
1

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.

Community
  • 1
  • 1
smoksnes
  • 10,509
  • 4
  • 49
  • 74
  • unfortunately it's not a restful service, it's just a normal call a url and receive data service, which use query strings – Hassan Faghihi Jun 13 '16 at 06:12
  • It should work anyway. You can still use the code provided by using parameters in the method signature. – smoksnes Jun 13 '16 at 06:14
  • here's waht i did: `var client2 = new RestClient(); RestRequest req = new RestRequest(command); var executeAsGet = client2.Execute(req).Data;` i tried to pass `command` to either of `RestClient` or `RestRequest`, i also tried to use both `CityVM`, and `CityResponseVM`, all of them returns `null`. – Hassan Faghihi Jun 13 '16 at 06:26
  • I've updated my answer. I think the problem is the JSON-format. – smoksnes Jun 13 '16 at 06:37
  • I suggest that you try to deserialize as Dictionary. – smoksnes Jun 13 '16 at 06:47
  • still null... NOTE: that in country it returns `{0:x, 1:y,...,StartTime:z, EndTime:w}` which it has two property doesn't match dictionary. second: for City, unlike the country, i have `{CityInfo:[ { CityCode:a11, Name:a12 }, { CityCode : a21, Name : a22 }, { CityCode : a31, Name : a32 }, ...], StartTime:b, EndTime:c}` – Hassan Faghihi Jun 13 '16 at 07:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/114500/discussion-between-smoksnes-and-deadmann). – smoksnes Jun 13 '16 at 07:04