Search code examples
c#jsonvisual-studiojson.netdynamic-variables

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 aNewtonsoft.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.


Solution

  • 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.