使用Linq,JSON.NET,C#查询JSON嵌套数组

Jaz*_*mov 2 c# linq json.net

这篇文章旨在提出一个与我最近的其他文章(从Hierarchical JSON Part II中挑选简单属性)有关的更直接的问题:

给定嵌套的JSON文档,如下所示:

{
  "Array1": {
    "Array1A": [
      { "Item1": "1" },
      { "Item2": "2" },
      { "Item3": "3" }
    ],
    "Array1B": [
      { "Item1": "1" },
      { "Item2": "2" },
      { "Item3": "3" }
    ]
  },
  "Array2": {
    "Array2A": [
      { "Item1": "1" },
      { "Item2": "2" },
      { "Item3": "3" }
    ]
  },
  "Array3": {
    "Array3A": [
      { "Item1": "1" },
      { "Item2": "2" },
      { "Item3": "3" }
    ],
    "Array3B": [
      { "Item1": "1" },
      { "Item2": "2" },
      {
        "Array3B1": [
          { "Item1": "1" },
          { "Item2": "2" },
          { "Item3": "3" }
        ]
      }
    ],
    "Array3C": [
      { "Item1": "1" },
      { "Item2": "2" },
      { "Item3": "3" }
    ]
  }
}
Run Code Online (Sandbox Code Playgroud)

(注意:以上已通过JSLint验证。)

注意JSON是动态的-我事先不知道会有多少个数组或数组嵌套的深度。

Goal: My goal is to represent each array (that is, Array1, Array2, Array3, Array3A, Array3B, and Array3B1) in a List<JObject> object. Each item in the list will be a collection of JProperty objects containing that array's string properties. Because the List doesn't itself model hierarchical data, I would need to add a synthetic property to each List<JObject> item that references the parent of that array. Thus, the parent of Array1 is an empty string; Array2 is Array1, Array3 is Array2, Array3A is Array3, Array3B is Array3, and Array 3B1 is Array3B...

Question: 1. How can I use C# Linq to create a List<JObject> object that looks like this:

list[0]: 
{"Name":"Array1","Parent":""}

list[1]:
{"Name":"Array1A","Item1":"1","Item2":"2","Item3":"3","Parent":"Array1"}

list[2]:
{"Name":"Array1B","Item1":"1","Item2":"2","Item3":"3","Parent":"Array1"}

list[3]:
{"Name":"Array2","Parent":""}

list[4]:
{"Name":"Array2A","Item1":"1","Item2":"2","Item3":"3","Parent":"Array2"}

list[5]:
{"Name":"Array3","Parent":""}

list[6]:
{"Name":"Array3A","Item1":"1","Item2":"2","Item3":"3","Parent":"Array3"}

list[7]:
{"Name":"Array3B","Item1":"1","Item2":"2","Parent":"Array3"}

list[8]:
{"Name":"Array3B1","Item1":"1","Item2":"2","Item3":"3","Parent":"ArrayB"}

list[9]:
{"Name":"Array3C","Item1":"1","Item2":"2","Item3":"3","Parent":"Array3"}
Run Code Online (Sandbox Code Playgroud)

Please note that:

  • Each List<JObject> contains only string properties.
  • 在list [7]处,缺少Item2之后的JSON令牌,因为它是一个数组。而是使用正确的父引用在list [8]中表示该项目。

Bri*_*ers 8

这样的事情怎么样:

List<JObject> list = 
    JObject.Parse(json)
           .Descendants()
           .Where(jt => jt.Type == JTokenType.Property && ((JProperty)jt).Value.HasValues)
           .Cast<JProperty>()
           .Select(prop =>
           {
               var obj = new JObject(new JProperty("Name", prop.Name));
               if (prop.Value.Type == JTokenType.Array)
               {
                   var items = prop.Value.Children<JObject>()
                                         .SelectMany(jo => jo.Properties())
                                         .Where(jp => jp.Value.Type == JTokenType.String);
                   obj.Add(items);
               }
               var parentName = prop.Ancestors()
                                    .Where(jt => jt.Type == JTokenType.Property)
                                    .Select(jt => ((JProperty)jt).Name)
                                    .FirstOrDefault();
               obj.Add("Parent", parentName ?? "");
               return obj;
           })
           .ToList();
Run Code Online (Sandbox Code Playgroud)

小提琴:https : //dotnetfiddle.net/FMxzls

如果您对LINQ-to-JSON不太熟悉,请按以下步骤进行分解:

  1. 将json字符串解析为JObject

    JObject.Parse(json)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 从该JObject中获取其所有后代JToken

           .Descendants()
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将该列表过滤为仅JProperty,其值具有子级

           .Where(jt => jt.Type == JTokenType.Property && ((JProperty)jt).Value.HasValues)
    
    Run Code Online (Sandbox Code Playgroud)
  4. 将JTokens转换为JProperty,以使其在下一步中更易于使用

           .Cast<JProperty>()
    
    Run Code Online (Sandbox Code Playgroud)
  5. 现在,对于我们选择的每个JProperty,将其转换如下:

           .Select(prop =>
           {
    
    Run Code Online (Sandbox Code Playgroud)
  6. 创建一个新的JObject并将JProperty的名称添加为Name新对象的属性

               var obj = new JObject(new JProperty("Name", prop.Name));
    
    Run Code Online (Sandbox Code Playgroud)
  7. 如果JProperty的值是一个数组...

               if (prop.Value.Type == JTokenType.Array)
               {
    
    Run Code Online (Sandbox Code Playgroud)
  8. 获取数组的所有直接子对象均为JObjects

                   var items = prop.Value.Children<JObject>()
    
    Run Code Online (Sandbox Code Playgroud)
  9. 从这些JObjects中,获取所有JProperties

                                         .SelectMany(jo => jo.Properties())
    
    Run Code Online (Sandbox Code Playgroud)
  10. 筛选那些JProperty,使其仅包含其值为字符串的JProperty)

                                         .Where(jp => jp.Value.Type == JTokenType.String);
    
    Run Code Online (Sandbox Code Playgroud)
  11. 将这些项目JProperties添加到我们之前创建的新JObject中

                    obj.Add(items);
                }
    
    Run Code Online (Sandbox Code Playgroud)
  12. 接下来,找到当前JProperty的第一个祖先JProperty并获取其名称

                var parentName = prop.Ancestors()
                                     .Where(jt => jt.Type == JTokenType.Property)
                                     .Select(jt => ((JProperty)jt).Name)
                                     .FirstOrDefault();
    
    Run Code Online (Sandbox Code Playgroud)
  13. 将父名称添加到我们正在构建的JObject中;如果没有父母,请使用空字符串

                obj.Add("Parent", parentName ?? "");
    
    Run Code Online (Sandbox Code Playgroud)
  14. 继续下一个转换

                return obj;
            })
    
    Run Code Online (Sandbox Code Playgroud)
  15. 最后,将我们构建的所有JObjects放入列表中。

            .ToList();
    
    Run Code Online (Sandbox Code Playgroud)