Cannot deserialize JSON array into type - Json.NET
Solution 1
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);
Solution 2
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.
tugberk
Senior Software Engineer and Tech Lead, with a growth mindset belief and 10+ years of practical software engineering experience including technical leadership and distributed systems. I have a passion to create impactful software products, and I care about usability, reliability, observability and scalability of the software systems that I work on, as much as caring about day-to-day effectiveness, productivity and happiness of the team that I work with. I occasionally speak at international conferences (tugberkugurlu.com/speaking), and write technical posts on my blog (tugberkugurlu.com). I currently work at Facebook as a Software Engineer. I used to work at Deliveroo as a Staff Software Engineer in the Consumer division, working on distributed backend systems which have high throughput, low latency and high availability needs. Before that, I used to work at Redgate as a Technical Lead for 4 years, where I led and line-managed a team of 5 Software Engineers. I was responsible for all aspects of the products delivered by the team from technical architecture to product direction. I was also a Microsoft MVP for 7 years between 2012-2019 on Microsoft development technologies.
Updated on February 27, 2020Comments
-
tugberk over 3 years
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.
-
svick over 11 yearsCan't you simplify deserializing the properties of
CountryModel
by usingserializer.Deserialize<CountryModel>(reader)
? -
Paul Tyng over 11 yearsMuch simpler than my version, nice, didn't know you could create the reader's off the built in objects.
-
Paul Tyng over 11 yearssvick'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 over 11 years@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. -
svick over 11 yearsRight, I didn't think about that.
-
tugberk over 11 yearsthis does not effect the deserialization process.