简体   繁体   中英

Casting json object to typed object with only a subset of properties

I am selecting a json object as follows:

var gridRenderer = json.SelectToken("$..gridRenderer").FirstOrDefault();

The line above gives the following subset of the JSON shown at the end of the question:

{
  "items": [
    {
      "gridVideoRenderer": { } // Many properties omitted.
    },
    // Other entries omitted.

Located at the path:

contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer

As you can see above there is a lot of data. I want to cast this to typed object with a sub set of properties:

var ee = gridRenderer.Cast<GridRenderSection>();

First I just wanted to get an array with objects with the videoid. I have following classes:

[JsonObject("gridRenderSection")]
public class GridRenderSection
{
     [JsonProperty("items")]
     public List<GridVideoRenderer> Items{ get; set; }
}

public class GridVideoRenderer
{
    [JsonProperty("videoId")]
    public string VideoId { get; set; }
}

But I get following error: "Unable to cast object of type 'Newtonsoft.Json.Linq.JArray' to type 'GridRenderSection'."

Normally you can cast subset of types objects, is that not possible with the Cast function, or am I missing something here?

Sample code (notice you can see json on the link provided in sample code or below):

var jsonStr = string.Empty;
using (WebClient client = new WebClient())
{
    jsonStr = client.DownloadString("https://srv-file2.gofile.io/download/J1iBAd/json1.json");
}

var json = JObject.Parse(jsonStr);

var gridRenderer = json.SelectToken("$..gridRenderer").FirstOrDefault();
var ee = gridRenderer.Cast<GridRenderSection>();

JSON:

{  
  "contents": {
    "twoColumnBrowseResultsRenderer": {
      "tabs": [
        {
          "tabRenderer": {
            "title": "Videoer",
            "selected": true,
            "content": {
              "sectionListRenderer": {
                "contents": [
                  {
                    "itemSectionRenderer": {
                      "contents": [
                        {
                          "gridRenderer": {
                            "items": [
                              {
                                "gridVideoRenderer": {
                                  "videoId": "lzSlEtuHgAU"
                                }
                              },
                              {
                                "gridVideoRenderer": {
                                  "videoId": "F5Opl1llzWw"
                                }
                              }
                            ]
                          }
                        }
                      ]
                    }
                  }
                ]            
              }
            }
          }
        }
      ]
    }
  }
}

You have a few problems here:

  1. Your query json.SelectToken("$..gridRenderer") is returning a single JToken corresponding to the following JSON object:

     { "items": [ { "gridVideoRenderer": { "videoId": "lzSlEtuHgAU" } }, { "gridVideoRenderer": { "videoId": "F5Opl1llzWw" } } ] } 

    You are trying to cast this to a GridRenderSection , but JToken has no explicit or implicit type conversion to arbitrary types -- only to simple IConvertible types. Instead you will need to deserialize to GridRenderSection using ToObject<GridRenderSection>() :

     var ee = gridRenderer.ToObject<GridRenderSection>(); 
  2. You are calling FirstOrDefault() on the return from SelectToken() :

     json.SelectToken("$..gridRenderer").FirstOrDefault(); 

    However, SelectToken() already returns a single JToken that matches the query, in this case a JObject . (If there is more than one match, SelectToken() throws an exception.) Later, when you do .FirstOrDefault() you are grabbing the first property of the object and trying to deserialize that single property into your data model, which is wrong.

    Generally, if you are calling SelectToken() there is no need to call .FirstOrDefault() ; it's more commonly applied to SelectTokens() when there might be many matches and only the first is needed.

  3. Your data model does not match your JSON. videoId is nested inside a container object gridVideoRenderer which needs to be modeled like so:

     public class GridRenderSection { [JsonProperty("items")] public List<GridRenderSectionItem> Items{ get; set; } } public class GridRenderSectionItem { public GridVideoRenderer gridVideoRenderer { get; set; } } public class GridVideoRenderer { [JsonProperty("videoId")] public string VideoId { get; set; } } 

Incidentally, if all you need are the videoId values, you could use the following SelectTokens() query to extract them directly:

var videoIds = json.SelectTokens("$..gridRenderer.items[*].gridVideoRenderer.videoId")
    .Select(t => t.ToString())
    .ToList();

Or if you want just a list of GridVideoRenderer objects:

var renderers = json.SelectTokens("$..gridRenderer.items[*].gridVideoRenderer")
    .Select(t => t.ToObject<GridVideoRenderer>())
    .ToList();

Demo fixed fiddle here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM