I am pulling down JSON data from a website and some of the fields are optional, meaning they do not always exist. When I try to query them, my program breaks. I believe I am checking for nulls incorrectly.
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "myToken");
httpClient.BaseAddress = new Uri("https://app.website.com/data/");
HttpResponseMessage response = httpClient.GetAsync("getData.json?thing=abc").Result;
response.EnsureSuccessStatusCode();
var result = JsonConvert.DeserializeObject<JToken>response.Content.ReadAsStringAsync().Result);
if (result.HasValues)
{
var count = result.SelectTokens("results.main[*].item").Count();
if (count > 0)
{
var total = result.SelectTokens("results.main[*].item.total").First() != null ? result.SelectTokens("results.main[*].item.total").First().ToString() : string.Empty;
}
}
}
It breaks when I try to query for total
even though I’m checking for nulls. Since it’s not always a guarantee that total
is returned, how can I check for it first?
The JSON array looks like this. Sometimes total
exists and other times it doesn’t.
{
"results": {
"copyright": "Copyright (c)",
"main": [{
"item": {
"name": fruit,
"date": 1900-01-01,
"total": 123456
}
}]
}
}
2
Answers
Your immediate problem is that you are trying to get the value of the first
"total"
by callingSelectTokens("...").First()
. The extension methodEnumerable.First()
will throw an exception if the enumerable is empty and so is not appropriate to use when querying for optional values. Instead, you could useFirstOrDefault()
orSingleOrDefault()
. (Enumerable.Count()
should be avoided when you only need to check if an enumerable has content, as it will enumerate the entire sequence.)Thus your code can be simplified to:
That being said, since you are expecting only one item in the
results.main[*]
array, you could just useSelectToken()
instead ofSelectTokens()
:Alternatively, if you the
results.main[*]
might have multiple items and only some have totals, you could use the JSONPath conditional operator to select them:Notes:
Calling
SelectToken()
orSelectTokens()
does incur a performance cost, so I would recommend against making the same query more than once just so that to do everything in one (long) line of code.As explained in Avoiding Deadlock with HttpClient,
httpClient.GetAsync(url).Result;
can sometimes result in deadlocks, so you might want to rewrite your method to be asynchronous.Demo fiddle here.
your json is not valid. But if you know how to fix it, you can get your data in one line of code.
It will be working without any exception if even the list "main" is not exist.
but if you need to know if an array exist and has at least one item
P.S. All explanations you can find @dbc answer