Nicholas Pickering Nicholas Pickering - 3 months ago 50
C# Question

JsonConvert.DeserializeObject generates a null dynamic object

I'm attempting to deserialize a response from a REST API using JSON.NET.

dynamic resultObject = JsonConvert.DeserializeObject(responseText);


I'd like to not have to define classes for the different kinds of responses the REST API returns, as it changes often. I'd like to take advantage of
dynamic
runtime variables.

In VS2015, I've added watchers to see the values directly after the line of code above executes.

resultObject
resolves to a null
object
, however, the watcher shows that the line code run directly results in a
Newtonsoft.Json.Linq.JObject
which is populated with the deserialized response string.

Why doesn't the dynamic var
resultObject
populate with the
JObject
?

var responseStream = e.Response?.GetResponseStream();
string responseText = "";

if (responseStream != null)
{
using (var reader = new StreamReader(responseStream))
{
responseText = reader.ReadToEnd();
}

dynamic responseObject = JsonConvert.DeserializeObject(responseText);

foreach (var error in responseObject["errors"].Children())
{
errors.Add(error.Val);
}

}


UPDATE:

Contents of the JSON to parse:

JSON updated to remove debug information - problem persists.

https://jsonblob.com/57cb00c7e4b0dc55a4f2abe9

UPDATE 2:

It appears that
JsonConvert.DeserializeObject()
is parsing my JSON to have extra brackets around the entire object.

String as it is generated from the response stream:

"{\"message\":\"422 Unprocessable Entity\",\"errors\":[[\"The email must be a valid email address.\"],[\"The password must be at least 8 characters.\"]],\"status_code\":422}"


value of JsonConvert.DeserializeObject():

{{
"message": "422 Unprocessable Entity",
"errors": [
[
"The email must be a valid email address."
],
[
"The password must be at least 8 characters."
]
],
"status_code": 422
}}


UPDATE 3:
Converting to
JObject.Parse
resulted in the same output.

I changed the variable from type
dynamic
to
JObject
- to accept the response from
JObject.Parse()
and still the variable is set to null.

dbc dbc
Answer

Your problem is that, in the following line, you are assuming that "errors" is an array of JSON objects, each with a property named "Val":

foreach (var error in responseObject["errors"].Children())
{
     errors.Add(error.Val);
}

However, "errors" is actually an array of arrays, and so error.Val evaluates to null. Instead you need to do something like:

var errors = new List<string>();

dynamic responseObject = JsonConvert.DeserializeObject(responseText);

foreach (dynamic errorList in responseObject["errors"].Children())
{
    foreach (dynamic error in errorList.Children())
    {
        errors.Add((string)error);
    }
}

Personally, however, I recommend avoiding use of dynamic because you lose the advantages of static, compile-time type checking. Code full of dynamic objects can also be hard to debug. Instead I would recommend the use of LINQ to JSON, specifically the SelectTokens() along with the JSONPath recursive descent operator ..* to pick out all string values inside the "errors" object like so:

var token = JToken.Parse(responseText);

var errors = token.SelectTokens("errors..*")    // Iterate through all descendants of the "errors" property
    .OfType<JValue>()                           // Filter those that are primitive values
    .Select(v => (string)v)                     // Convert to their string values
    .ToList();                                  // And evaluate the query as a list.

Example fiddle.