Querying JSON Nested Arrays with Linq, JSON.NET, C#

前端 未结 1 618
情话喂你
情话喂你 2020-12-20 02:36

This post is an effort to ask a more direct question related to my other recent post (Picking Out Simple Properties from Hierarchical JSON Part II):

Given nested JSO

相关标签:
1条回答
  • 2020-12-20 02:48

    How about something like this:

    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();
    

    Fiddle: https://dotnetfiddle.net/FMxzls

    If you're not that familiar with LINQ-to-JSON, here's how it breaks down:

    1. Parse the json string into a JObject

      JObject.Parse(json)
      
    2. From that JObject, get all of its descendant JTokens

             .Descendants()
      
    3. Filter that list to only JProperties whose values have children

             .Where(jt => jt.Type == JTokenType.Property && ((JProperty)jt).Value.HasValues)
      
    4. Cast the JTokens to JProperties to make them easier to work with in the next step

             .Cast<JProperty>()
      
    5. Now, for each JProperty we selected, transform it as follows:

             .Select(prop =>
             {
      
    6. Create a new JObject and add the JProperty's name as the Name property of the new object

                 var obj = new JObject(new JProperty("Name", prop.Name));
      
    7. If the value of the JProperty is an array...

                 if (prop.Value.Type == JTokenType.Array)
                 {
      
    8. Get all the direct children of the array which are JObjects

                     var items = prop.Value.Children<JObject>()
      
    9. From those JObjects, get all the JProperties

                                           .SelectMany(jo => jo.Properties())
      
    10. Filter those JProperties to include only the ones whose values are strings)

                                           .Where(jp => jp.Value.Type == JTokenType.String);
      
    11. Add these item JProperties to the new JObject we created earlier

                      obj.Add(items);
                  }
      
    12. Next, find the first ancestor JProperty of the current JProperty and get its name

                  var parentName = prop.Ancestors()
                                       .Where(jt => jt.Type == JTokenType.Property)
                                       .Select(jt => ((JProperty)jt).Name)
                                       .FirstOrDefault();
      
    13. Add the parent name to the JObject we're are building; use an empty string if there was no parent

                  obj.Add("Parent", parentName ?? "");
      
    14. Continue with the next transform

                  return obj;
              })
      
    15. Lastly put all the JObjects we built into a list.

              .ToList();
      
    0 讨论(0)
提交回复
热议问题