Reputation: 4959
I'm trying to deserialize some objects nested inside a Json response using Newtonsoft.Json. I want to deserialize the following Jsons Term
objects into a list. I have many Term
objects in the Json response, so performance and compactness is important to me. I also would only like to define the Term
class as I do not care about the other data for the time being.
I have a model defined for Term:
public class Term
{
public string Known { get; set; }
public string Word { get; set; }
}
My Json looks like this:
{
"myName":"Chris",
"mySpecies":"Cat",
"myTerms":
[
{
"Term":
{
"Known":"true",
"Word":"Meow"
}
},
{
"Term":
{
"Known":"false",
"Word":"Bark"
}
}
]
}
My C# deserializing code:
var response = await httpClient.GetAsync(uri);
string responseString = response.Content.ReadAsStringAsync().GetResults();
var searchTermList = JsonConvert.DeserializeObject<List<Term>>(responseString);
The problem/error I'm receiving is, not sure how I can get these terms from the json response:
{Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current
JSON object (e.g. {"name":"value"}) into type
'System.Collections.Generic.List`1[CoreProject.Models.Term]' because
the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or
change the deserialized type so that it is a normal .NET type (e.g. not a
primitive type like integer, not a collection type like an array or List<T>)
that can be deserialized from a JSON object. JsonObjectAttribute can also be
added to the type to force it to deserialize from a JSON object.
Any suggestions would be greatly appreciated :)
Upvotes: 1
Views: 3008
Reputation: 116805
You are getting that error because you are trying to deserialize the JSON into a List<T>
for some T
(specifically Term
), but the root JSON container is not an array, it is an object -- an unordered set of key/value pairs surrounded by {
and }
-- that contains a fairly deeply embedded collection of objects corresponding to your Term
.
Given that, you could use http://json2csharp.com/ or Paste JSON as Classes to auto-generate a complete data model corresponding to your JSON, then deserialize to that model and select out the interesting portions.
If, however, you don't want to define a complete data model, you can selectively deserialize only the relevant portions by loading your JSON into an intermediate JToken
hierarchy and then using
SelectTokens()
:
var root = JToken.Parse(responseString);
var searchTermList = root.SelectTokens("myTerms[*].Term")
.Select(t => t.ToObject<Term>())
.ToList();
Notes:
The query string "myTerms[*].Term"
contains the JSONPath wildcard operator [*]
. This operator matches all array elements under the parent element "myTerms"
.
Json.NET supports JSONPath syntax as documented in Querying JSON with JSONPath.
If the JSON is more complex than is shown in your question, you can use the JSONPath recursive descent operator ...
instead to find Term
objects at any level in the JSON object hierarchy, e.g.:
var searchTermList = root.SelectTokens("..Term")
.Select(t => t.ToObject<Term>())
.ToList();
Once the relevant JSON objects have been selected you can use Jtoken.ToObject<Term>()
to deserialize each one to your final c# model.
Sample fiddle.
Upvotes: 4
Reputation: 1372
Give this a try
public class Term
{
public string Known { get; set; }
public string Word { get; set; }
}
public class Response
{
public List<TermWrapper> MyTerms { get; set; }
}
public class TermWrapper
{
public Term Term { get; set; }
}
...
var response = await httpClient.GetAsync(uri);
string responseString = response.Content.ReadAsStringAsync().GetResults();
var searchTermList = JsonConvert
.DeserializeObject<Response>(responseString)
.MyTerms
.Select(x => x.Term);
You have to consider the full structure of the JSON. You have an object with 3 properties. The one you are interested in is an Array of Objects, but the objects are not terms, they are objects with a property called "Term". That property itself is of type Term
. By creating the classes with similar structure you can then pull all the necessary data out of the structure.
Upvotes: 0