I am having difficulty deserializing a JSON string retrieved from a web source. I receive an error stating the following:
Unhandled Exception: Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}
) into type 'NewsApp.ComplexTypes.NYTimes.Keyword' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
This also happens with a property named Byline. Here is the json2csharp models that were produced:
public class Headline
{
public string main { get; set; }
public string kicker { get; set; }
}
public class Keywords
{
public string rank { get; set; }
public string name { get; set; }
public string value { get; set; }
}
public class Byline
{
public string organization { get; set; }
public string original { get; set; }
public List<string> person { get; set; }
}
public class Multimedia
{
public string url { get; set; }
public string format { get; set; }
public int height { get; set; }
public int width { get; set; }
public string type { get; set; }
public string subtype { get; set; }
public string caption { get; set; }
public string copyright { get; set; }
}
public class Doc
{
public string web_url { get; set; }
public string snippet { get; set; }
public string lead_paragraph { get; set; }
public string @abstract { get; set; }
public string print_page { get; set; }
public List<string> blog { get; set; }
public string source { get; set; }
public Headline headline { get; set; }
public Keywords keywords { get; set; }
public string pub_date { get; set; }
public string document_type { get; set; }
public string news_desK { get; set; }
public string section_name { get; set; }
public string subsection_name { get; set; }
public Byline byline { get; set; }
public string type_of_material { get; set; }
public string _id { get; set; }
public string word_count { get; set; }
public string slideshow_credits { get; set; }
public List<Multimedia> multimedia { get; set; }
}
public class Meta
{
public int hits { get; set; }
public int time { get; set; }
public int offset { get; set; }
}
public class Response
{
public List<Doc> docs { get; set; }
public Meta meta { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}
And here is the structure of the JSON returned from the API call.
{
"response": {
"docs": [
{
"web_url": "string",
"snippet": "string",
"lead_paragraph": "string",
"abstract": "string",
"print_page": "string",
"blog": [
""
],
"source": "string",
"headline": {
"main": "string",
"kicker": "string"
},
"keywords": {
"rank": "string",
"name": "string",
"value": "string"
},
"pub_date": "string",
"document_type": "string",
"news_desK": "string",
"section_name": "string",
"subsection_name": "string",
"byline": {
"organization": "string",
"original": "string",
"person": [
""
]
},
"type_of_material": "string",
"_id": "string",
"word_count": "string",
"slideshow_credits": "string",
"multimedia": [
{
"url": "string",
"format": "string",
"height": 0,
"width": 0,
"type": "string",
"subtype": "string",
"caption": "string",
"copyright": "string"
}
]
}
],
"meta": {
"hits": 0,
"time": 0,
"offset": 0
}
}
}
Here is how I have defined my RootObject and Doc classes
public class RootObject
{
public Response response { get; set; }
}
public class Doc
{
public Doc()
{
blog = new List<string>();
headline = new NYTimes.Headline();
keywords = new List<Keyword>();
multimedia = new List<Multimedia>();
byline = new Byline();
}
public string web_url { get; set; }
public string snippet { get; set; }
public string lead_paragraph { get; set; }
public string @abstract { get; set; }
public string print_page { get; set; }
public List<string> blog { get; set; }
public string source { get; set; }
public Headline headline { get; set; }
public List<Keyword> keywords { get; set; }
public string pub_date { get; set; }
public string document_type { get; set; }
public string news_desK { get; set; }
public string section_name { get; set; }
public string subsection_name { get; set; }
public Byline byline { get; set; }
public string type_of_material { get; set; }
public string _id { get; set; }
public string word_count { get; set; }
public string slideshow_credits { get; set; }
public List<Multimedia> multimedia { get; set; }
}
And here is a small sample data set.
{
"copyright": "Copyright (c) 2013 The New York Times Company. All Rights Reserved.",
"response": {
"meta": {
"hits": 7644
},
"docs": [{
"web_url": "https://www.nytimes.com/slideshow/2016/01/01/nytnow/the-week-on-instagram.html",
"snippet": "Photos posted on @nytimes during the last week of 2015 took followers from an island in Antarctica to Times Square, before the ball dropped....",
"lead_paragraph": "Photos posted on @nytimes during the last week of 2015 took followers from an island in Antarctica to Times Square, before the ball dropped.",
"abstract": null,
"print_page": null,
"blog": [],
"source": "The New York Times",
"multimedia": [{
"width": 190,
"url": "images/2016/01/01/nytnow/the-week-on-instagram-slide-8Y3Z/the-week-on-instagram-slide-8Y3Z-thumbWide.jpg",
"height": 126,
"subtype": "wide",
"legacy": {
"wide": "images/2016/01/01/nytnow/the-week-on-instagram-slide-8Y3Z/the-week-on-instagram-slide-8Y3Z-thumbWide.jpg",
"wideheight": "126",
"widewidth": "190"
},
"type": "image"
},
{
"width": 600,
"url": "images/2016/01/01/nytnow/the-week-on-instagram-slide-8Y3Z/the-week-on-instagram-slide-8Y3Z-articleLarge.jpg",
"height": 600,
"subtype": "xlarge",
"legacy": {
"xlargewidth": "600",
"xlarge": "images/2016/01/01/nytnow/the-week-on-instagram-slide-8Y3Z/the-week-on-instagram-slide-8Y3Z-articleLarge.jpg",
"xlargeheight": "600"
},
"type": "image"
},
{
"width": 75,
"url": "images/2016/01/01/nytnow/the-week-on-instagram-slide-8Y3Z/the-week-on-instagram-slide-8Y3Z-thumbStandard.jpg",
"height": 75,
"subtype": "thumbnail",
"legacy": {
"thumbnailheight": "75",
"thumbnail": "images/2016/01/01/nytnow/the-week-on-instagram-slide-8Y3Z/the-week-on-instagram-slide-8Y3Z-thumbStandard.jpg",
"thumbnailwidth": "75"
},
"type": "image"
}
],
"headline": {
"main": "The Week on Instagram"
},
"keywords": [],
"pub_date": "2016-01-01T00:00:00Z",
"document_type": "multimedia",
"news_desk": "NYT Now",
"section_name": "NYT Now",
"subsection_name": null,
"byline": [],
"type_of_material": "Slideshow",
"_id": "5687cefb38f0d82225327003",
"word_count": "24",
"slideshow_credits": "Ben C. Solomon/The New York Times"
},
{
"web_url": "https://wordplay.blogs.nytimes.com/2016/01/01/mass-master/",
"snippet": "David Phillips provides our first Saturday challenge of 2016....",
"lead_paragraph": null,
"abstract": "David Phillips provides our first Saturday challenge of 2016.",
"print_page": null,
"blog": [],
"source": "The New York Times",
"multimedia": [{
"width": 190,
"url": "images/2016/01/01/crosswords/0101yalejpg/0101yalejpg-thumbWide.jpg",
"height": 126,
"subtype": "wide",
"legacy": {
"wide": "images/2016/01/01/crosswords/0101yalejpg/0101yalejpg-thumbWide.jpg",
"wideheight": "126",
"widewidth": "190"
},
"type": "image"
},
{
"width": 600,
"url": "images/2016/01/01/crosswords/0101yalejpg/0101yalejpg-articleLarge.jpg",
"height": 400,
"subtype": "xlarge",
"legacy": {
"xlargewidth": "600",
"xlarge": "images/2016/01/01/crosswords/0101yalejpg/0101yalejpg-articleLarge.jpg",
"xlargeheight": "400"
},
"type": "image"
},
{
"width": 75,
"url": "images/2016/01/01/crosswords/0101yalejpg/0101yalejpg-thumbStandard.jpg",
"height": 75,
"subtype": "thumbnail",
"legacy": {
"thumbnailheight": "75",
"thumbnail": "images/2016/01/01/crosswords/0101yalejpg/0101yalejpg-thumbStandard.jpg",
"thumbnailwidth": "75"
},
"type": "image"
}
],
"headline": {
"main": "Mass Master",
"kicker": "Wordplay"
},
"keywords": [{
"rank": "1",
"name": "subject",
"value": "Crossword Puzzles"
}],
"pub_date": "2016-01-01T22:00:56Z",
"document_type": "blogpost",
"news_desk": "Business",
"section_name": "Crosswords & Games",
"subsection_name": null,
"byline": {
"person": [{
"organization": "",
"role": "reported",
"firstname": "Deb",
"rank": 1,
"lastname": "AMLEN"
}],
"original": "By DEB AMLEN"
},
"type_of_material": "Blog",
"_id": "56873d8d38f0d82225326f90",
"word_count": "618",
"slideshow_credits": null
},
{
"web_url": "https://krugman.blogs.nytimes.com/2016/01/01/friday-night-music-more-wild-reeds/",
"snippet": "Harmony to soothe the soul....",
"lead_paragraph": null,
"abstract": "Harmony to soothe the soul.",
"print_page": null,
"blog": [],
"source": "The New York Times",
"multimedia": [],
"headline": {
"main": "Friday Night Music: More Wild Reeds",
"kicker": "Paul Krugman"
},
"keywords": [],
"pub_date": "2016-01-01T21:17:09Z",
"document_type": "blogpost",
"news_desk": "OpEd",
"section_name": "Opinion",
"subsection_name": null,
"byline": {
"person": [{
"organization": "",
"role": "reported",
"firstname": "Paul",
"rank": 1,
"lastname": "KRUGMAN"
}],
"original": "By PAUL KRUGMAN"
},
"type_of_material": "Blog",
"_id": "5687337938f0d82225326f8b",
"word_count": "13",
"slideshow_credits": null
}
]
}
}
I have attempted to decorate the classes in question with the [JsonArray(AllowNullItems = true)] attribute but to no avail. The only thing that results in no exceptions, is when I change the variables explicitly declared as class objects to object, but this isn't exactly ideal.
Any help would be greatly appreciated. I just can't seem to get this deserialization working.
The call to deserialize the json.
public void GetThisMonthsArticles()
{
string endpoint = string.Format(Endpoint, new object[] { DateTime.Now.Year, DateTime.Now.Month, Resources.APIKeys.NYTimesAPI });
HttpClient client = HttpPersistentClient.GetHttpClient(endpoint);
WebClient wc = new WebClient();
string json = wc.DownloadString(endpoint);
RootObject = JsonConvert.DeserializeObject<RootObject>(json);
}
keywordsproperty is an array ("keywords": [],), and your your model expects an object (public Keywords keywords).List<Keyword>,IList<Keyword>.Keyword,Dictionary<string, List<Keyword>>,object,List<object>. The only one that was remotely successful wasList<object>.keywordsas being a single object; the second one shows it as being an array of objects. Which one is the one you are trying to deserialize? Or is it both of them? If both, you will need a JsonConverter to solve this. See How to handle both a single item and an array for the same property using JSON.net