首页 > 解决方案 > 将 json 对象转换为仅具有属性子集的类型化对象

问题描述

我正在选择一个 json 对象,如下所示:

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

上面的行给出了问题末尾显示的 JSON 的以下子集:

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

位于路径:

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

正如您在上面看到的,有很多数据。我想将其转换为具有一组属性的类型化对象:

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

首先,我只想获得一个带有 videoid 对象的数组。我有以下课程:

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

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

但我收到以下错误:“无法将'Newtonsoft.Json.Linq.JArray'类型的对象转换为'GridRenderSection'。”

通常你可以转换类型对象的子集,这对于 Cast 函数是不可能的,还是我在这里遗漏了什么?

示例代码(请注意,您可以在示例代码或下面提供的链接上看到 json):

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"
                                }
                              }
                            ]
                          }
                        }
                      ]
                    }
                  }
                ]            
              }
            }
          }
        }
      ]
    }
  }
}

标签: c#json.net

解决方案


你在这里有几个问题:

  1. 您的查询json.SelectToken("$..gridRenderer")返回一个JToken对应于以下 JSON 对象的单个:

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

    您正在尝试将其强制转换为 a GridRenderSection,但JToken没有显式或隐式类型转换为任意类型 - 仅转换为简单IConvertible类型。相反,您需要反序列化GridRenderSection使用ToObject<GridRenderSection>()

    var ee = gridRenderer.ToObject<GridRenderSection>();
    
  2. 您要求FirstOrDefault()从以下位置返回SelectToken()

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

    但是,SelectToken()已经返回了一个与查询匹配的单曲 JToken,在本例中为 a JObject。(如果有多个匹配项,则SelectToken()抛出异常。)稍后,当您.FirstOrDefault()获取对象的第一个属性并尝试将单个属性反序列化到您的数据模型中时,这是错误的。

    一般来说,如果您正在调用SelectToken(),则无需调用.FirstOrDefault();它更常应用于SelectTokens()可能有很多匹配并且只需要第一个匹配的情况。

  3. 您的数据模型与您的 JSON 不匹配。 videoId嵌套在gridVideoRenderer需要像这样建模的容器对象中:

    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; }
    }
    

顺便说一句,如果您只需要这些videoId值,则可以使用以下SelectTokens()查询直接提取它们:

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

或者,如果您只想要一个GridVideoRenderer对象列表:

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

演示固定小提琴here


推荐阅读