Search code examples
json.netjson-deserializationjsonconvert

Why does assigning a JArray to a var fail when I call JsonConvert.DeserializeObject()?


On this line of code:

var arr = JsonConvert.DeserializeObject<JArray>(s);

...I am getting, "Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'Newtonsoft.Json.Linq.JArray'."

I changed that line to this:

JArray arr = JsonConvert.DeserializeObject<JArray>(s);      

...and got the same err msg.

I changed it to this:

var arr = JsonConvert.DeserializeObject<JObject>(s);

...and it wouldn't even compile.

The value of what has been read by the call (in string s) at this point is:

{"id":347745,"results":[{"iso_3166_1":"US","release_dates":[{"certification":"","iso_639_1":"","note":"","release_date":"1936-12-12T00:00:00.000Z","type":3}]}]}

All I want from it is the value for "certification"; In this case, the certification value is an empty string ("certification":"")

In context, the code is:

. . .
try
{
    var webRequest = (HttpWebRequest)WebRequest.Create(RESTStringToGetMPAARatingForMovieId);
    webRequest.Method = "GET";  
    var webResponse = (HttpWebResponse)webRequest.GetResponse();
    if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
    {
        StreamReader streamReader = new StreamReader(webResponse.GetResponseStream());
        string s = streamReader.ReadToEnd();
        var arr = JsonConvert.DeserializeObject<JArray>(s);
        //JArray arr = JsonConvert.DeserializeObject<JArray>(s);
        //var arr = JsonConvert.DeserializeObject<JObject>(s);
        foreach (JObject obj in arr)
        {
            _currentMPAARating = (string)obj["certification"];
            . . .
        }
    }
    else
    {
        MessageBox.Show(string.Format("Status code == {0}, Content length == {1}",
          webResponse.StatusCode, webResponse.ContentLength));
    }    
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}

Solution

  • Your JSON is not an array, it is an object which contains an array (results). But it's actually more complicated than that: the certification string you seek is nested even further down inside a second release_dates array.

    If you take your JSON and reformat it using a JSON validator/beautifier, it should become more clear:

    {
      "id": 347745,
      "results": [
        {
          "iso_3166_1": "US",
          "release_dates": [
            {
              "certification": "",
              "iso_639_1": "",
              "note": "",
              "release_date": "1936-12-12T00:00:00Z",
              "type": 3
            }
          ]
        }
      ]
    }
    

    So to get the data you are looking for using regular foreach loops, you would need code like this:

    var obj = JsonConvert.DeserializeObject<JObject>(s);
    var resultArr = (JArray)obj["results"];
    foreach (JObject resultObj in resultArr)
    {
        var releaseDatesArr = (JArray)resultObj["release_dates"];
        foreach (JObject releaseDateObj in releaseDatesArr)
        {
            _currentMPAARating = (string)releaseDateObj["certification"];
            // ...
        }
    }
    

    Fiddle: https://dotnetfiddle.net/SMzQTw

    If all you need is the one item, here's a shortcut. Use the SelectToken method with the recursive descent operator (..) like this:

    var obj = JsonConvert.DeserializeObject<JObject>(s);
    _currentMPAARating = (string)obj.SelectToken("..certification");
    

    Fiddle: https://dotnetfiddle.net/S1ScLO

    But note the above will only return the first match. If you are expecting multiple certifications, you can use SelectTokens (plural) instead:

    var obj = JsonConvert.DeserializeObject<JObject>(s);
    var ratings = obj.SelectTokens("..certification").Select(t => (string)t).ToList();
    

    Fiddle: https://dotnetfiddle.net/zyjNnJ