Querying and Filtering Array of JObjects with Linq

时光毁灭记忆、已成空白 提交于 2019-12-20 01:52:12

问题


I suppose this is another entry in my series of questions, but I'm stuck again. This time, I'm having trouble working with a JArray of JObjects and determining the Property.Value type for each element in the JArray...

My code is here: https://dotnetfiddle.net/bRcSAQ

The difference between my previous questions and this question is that my outer Linq query gets both JObject and JArray tokens, so that's why I have an if (jo is JObject) at Line 40 and a if (jo is JArray) at Line 48.

Once I know I have a JArray of <JObjects>, I have code that looks like this (Lines 48-):

    if (jo is JArray)
    {
        var items = jo.Children<JObject>();                     
        // return a JObject object
    }

When I use a debugger and look at items, I see that it contains 3 JObject objects--one for Item_3A1, Item_3A2, and Item3A3. But I need to know the JTokenType for each JProperty.Value because I am interested only in Property values of type JTokenType.String.

So I tried:

// doesn't work :(
var items = jo.Children<JObject>()
              .Where(p => p.Value.Type == JTokenType.String);

The compiler red-lines the Value property with the error CS0119 'JToken.Value<T>(object)' is a method, which is not valid in the given context.

I realize that "p" in the Linq express is not a JProperty. I guess it's a JObject. And I don't know how to cast "p" so that I can examine the type of JProperty object it represents.

Ultimately, I need the code for JArray processing (starting at Line 48) to add an return a JObject that contains an JSON array composed only of JProperty objects of type JTokenType.String. This means that given the sample JSON, it first should return a JObject holding these JSON properties:

{ ""Item_3A1"": ""Desc_3A1"" },
{ ""Item_3A2"": ""Desc_3A2"" },
{ ""Item_3A3"": ""Desc_3A3"" }

On the next iteration, it should return a JObject holding these JSON properties (notice that the nested Array3B1 properties are omitted because Array3B1 is not a JProperty with a Value type of JTokenType.String):

{ ""Item_3B1"": ""Desc_3B1"" },
{ ""Item_3B2"": ""Desc_3B2"" },

The third iteration would contain:

{ ""Item_3B11"": ""Desc_3B11"" },
{ ""Item_3B12"": ""Desc_3B12"" },
{ ""Item_3B13"": ""Desc_3B13"" }

And the fourth (final) iteration would contain:

{ ""Item_3C1"": ""Desc_3C1"" },
{ ""Item_3C2"": ""Desc_3C2"" },
{ ""Item_3C3"": ""Desc_3C3"" }

This might be my final hurdle in this "series".

Sincere thanks to anyone who can and will help--and a special thanks again to users "Brian Rogers" and "dbc" for their truly amazing JSON.NET/Linq knowledge.


回答1:


This produces the output you require:

var root = (JContainer)JToken.Parse(json);
var query = root.Descendants()
    .Where(jt => (jt.Type == JTokenType.Object) || (jt.Type == JTokenType.Array))
    .Select(jo =>
        {
            if (jo is JObject)
            {
                if (jo.Parent != null && jo.Parent.Type == JTokenType.Array)
                    return null;
                // No help needed in this section               
                // populate and return a JObject for the List<JObject> result 
                // next line appears for compilation purposes only--I actually want a populated JObject to be returned
                return new JObject();
            }

            if (jo is JArray)
            {
                var items = jo.Children<JObject>().SelectMany(o => o.Properties()).Where(p => p.Value.Type == JTokenType.String);
                return new JObject(items);
            }
            return null;
        })
    .Where(jo => jo != null)
    .ToList();

Here I use SelectMany() to flatten the nested enumeration of properties of the enumeration of child objects of jo to a single enumeration of all properties of child objects. o => o.Properties() is a lambda expression mapping the JObject o to its collection of properties, and p => p.Value.Type == JTokenType.String is another lambda mapping a property p (generated by the previous SelectMany clause) to a true/false value indicating whether the property has a string value. Both o and p are lambda input parameters that are implicitly typed.

Also, in the // No help needed in this section section, objects whose parents are arrays are skipped, since they will get collected by the (jo is JArray) clause.

Note that if different children of the jo array happen to have identical property names, the JObject constructor may throw a duplicated key exception.

Forked fiddle.



来源:https://stackoverflow.com/questions/38670781/querying-and-filtering-array-of-jobjects-with-linq

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!