使用Linq查询和过滤JObject数组

时间:2016-07-30 05:50:23

标签: c# linq json.net

我想这是我系列问题中的另一个条目,但我又被卡住了。这次,我在使用JOA的JArray时遇到了麻烦,并为JArray中的每个元素确定了Property.Value类型......

我的代码在这里: https://dotnetfiddle.net/bRcSAQ

我之前的问题和这个问题之间的区别在于我的外部Linq查询同时获得了JObject和JArray令牌,这就是为什么我在第40行和if (jo is JObject)有一个if (jo is JArray)的原因在第48行。

一旦我知道我有一个<JObjects>的JArray,我的代码就像这样(第48行 - ):

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

当我使用调试器并查看项目时,我看到它包含3个JObject对象 - 一个用于Item_3A1,Item_3A2和Item3A3。但我需要知道每个JProperty.Value的JTokenType,因为我只对JTokenType.String类型的Property值感兴趣。

所以我试过了:

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

编译器使用错误CS0119 'JToken.Value<T>(object)' is a method, which is not valid in the given context.

对Value属性进行红线处理

我意识到&#34; p&#34;在林克快递不是JProperty。我猜它是一个JObject。而且我不知道如何演员&#34; p&#34;这样我就可以检查它所代表的JProperty对象的类型。

最终,我需要JArray处理的代码(从第48行开始)添加一个返回一个JObject,它包含一个仅由JTokenType.String类型的JProperty对象组成的JSON数组。这意味着给定样本JSON,它首先应该返回一个持有这些JSON属性的JObject:

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

在下一次迭代中,它应该返回一个持有这些JSON属性的JObject(请注意,嵌套的Array3B1属性被省略,因为Array3B1不是值类型为JTokenType.String的JProperty):

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

第三次迭代将包含:

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

第四次(最终)迭代将包含:

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

这可能是我在这个&#34;系列&#34;中的最后一道障碍。

真诚地感谢能够并且将会提供帮助的任何人 - 并再次特别感谢用户&#34; Brian Rogers&#34;和&#34; dbc&#34;因为他们真正令人惊叹的JSON.NET/Linq知识。

1 个答案:

答案 0 :(得分:2)

这会产生您需要的输出:

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

在这里,我使用SelectMany()jo的子对象枚举属性的嵌套枚举展平为子对象的所有属性的单个枚举。 o => o.Properties()lambda expressionJObject o映射到其属性集合,p => p.Value.Type == JTokenType.String是映射属性p的另一个lambda(由前一个{{1}生成}}子句)指示属性是否具有字符串值的true / false值。 SelectManyo都是lambda输入参数implicitly typed

此外,在p部分中,将跳过父项为数组的对象,因为它们将被// No help needed in this section子句收集。

请注意,如果(jo is JArray)数组的不同子节点碰巧具有相同的属性名称,则jo构造函数可能会抛出重复的键异常。

分叉fiddle