10

I am trying to deserialize a json data into a model class but I am failing. Here is what I do:

 public CountryModel GetCountries() { using (WebClient client = new WebClient()) { var result = client.DownloadString("http://api.worldbank.org/incomeLevels/LIC/countries?format=json"); var output = JsonConvert.DeserializeObject<List<CountryModel>>(result); return output.First(); } } 

This is how my model looks like:

public class CountryModel { public int Page { get; set; } public int Pages { get; set; } public int Per_Page { get; set; } public int Total { get; set; } public List<Country> Countries { get; set; } } public class Country { public int Id { get; set; } public string Iso2Code { get; set; } public string Name { get; set; } public Region Region { get; set; } } public class Region { public int Id { get; set; } public string Value { get; set; } } 

You can see the Json I am getting here: http://api.worldbank.org/incomeLevels/LIC/countries?format=json

This is the error I get:

Cannot deserialize JSON array into type 'Mvc4AsyncSample.Models.CountryModel'. Line 1, position 1.

5

3 Answers 3

17

You have to write a custom JsonConverter:

 public class CountryModelConverter : JsonConverter { public override bool CanConvert(Type objectType) { if (objectType == typeof(CountryModel)) { return true; } return false; } public override object ReadJson(JsonReader reader, Type objectType , object existingValue, JsonSerializer serializer) { reader.Read(); //start array //reader.Read(); //start object JObject obj = (JObject)serializer.Deserialize(reader); //{"page":1,"pages":1,"per_page":"50","total":35} var model = new CountryModel(); model.Page = Convert.ToInt32(((JValue)obj["page"]).Value); model.Pages = Convert.ToInt32(((JValue)obj["pages"]).Value); model.Per_Page = Int32.Parse((string) ((JValue)obj["per_page"]).Value); model.Total = Convert.ToInt32(((JValue)obj["total"]).Value); reader.Read(); //end object model.Countries = serializer.Deserialize<List<Country>>(reader); reader.Read(); //end array return model; } public override void WriteJson(JsonWriter writer, object value , JsonSerializer serializer) { throw new NotImplementedException(); } } 

And tag the CountryModel with that converter (I also had to switch some int to string):

 [JsonConverter(typeof(CountryModelConverter))] public class CountryModel { public int Page { get; set; } public int Pages { get; set; } public int Per_Page { get; set; } public int Total { get; set; } public List<Country> Countries { get; set; } } public class Country { public string Id { get; set; } public string Iso2Code { get; set; } public string Name { get; set; } public Region Region { get; set; } } public class Region { public string Id { get; set; } public string Value { get; set; } } 

Then you should be able to deserialize like this:

var output = JsonConvert.DeserializeObject<CountryModel>(result); 
4
  • Can't you simplify deserializing the properties of CountryModel by using serializer.Deserialize<CountryModel>(reader)?
    – svick
    CommentedFeb 26, 2012 at 12:50
  • svick's answer is better if you only need to read, the JsonConverter is only really necessary if you have to go both ways.
    – Paul Tyng
    CommentedFeb 26, 2012 at 12:53
  • 2
    @svick I thought about that, the problem is that CountryModel has the attribute, so it gets in to infinite loop, it would be simplified if the object model changed but I was trying to keep to his object model.
    – Paul Tyng
    CommentedFeb 26, 2012 at 12:53
  • Right, I didn't think about that.
    – svick
    CommentedFeb 26, 2012 at 12:55
12

This looks like a (not very good) attempt at representing XML in JSON. The JSON looks like this:

[ { "page": 1, … }, [ { "id": "AFG", "name": "Afghanistan", … }, { "id": "BDI", "name": "Burundi", … }, … ] ] 

While a reasonable JSON (that would incidentally map to your model nicely) would look like this:

{ "page": 1, …, "countries": [ { "id": "AFG", "name": "Afghanistan", … }, { "id": "BDI", "name": "Burundi", … }, … ] } 

If you are sure you want to use JSON (and not XML), you can do it by first deserializing the JSON into JSON.NET's object model and then deserialize that into your model:

var json = client.DownloadString("http://api.worldbank.org/incomeLevels/LIC/countries?format=json"); var array = (JArray)JsonConvert.DeserializeObject(json); var serializer = new JsonSerializer(); var countryModel = serializer.Deserialize<CountryModel>(array[0].CreateReader()); countryModel.Countries = serializer.Deserialize<List<Country>>(array[1].CreateReader()); return countryModel; 

Don't forget to change your Id properties to string, because that's what they are.

1
  • Much simpler than my version, nice, didn't know you could create the reader's off the built in objects.
    – Paul Tyng
    CommentedFeb 26, 2012 at 12:50
-1

Your model doesn't match the JSON structure. It looks like your missing the last 6 properties.

{ "id": "AFG", "iso2Code": "AF", "name": "Afghanistan", "region": { "id": "SAS", "value": "South Asia" }, "adminregion": { "id": "SAS", "value": "South Asia" }, "incomeLevel": { "id": "LIC", "value": "Low income" }, "lendingType": { "id": "IDX", "value": "IDA" }, "capitalCity": "Kabul", "longitude": "69.1761", "latitude": "34.5228" 

}

1
  • 7
    this does not effect the deserialization process.
    – tugberk
    CommentedFeb 27, 2012 at 8:28

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.