【问题标题】:Querying and Filtering Array of JObjects with Linq使用 Linq 查询和过滤对象数组
【发布时间】:2016-12-04 21:03:11
【问题描述】:

我想这是我系列问题中的另一个条目,但我又被困住了。这一次,我在处理 JObject 的 JArray 和确定 JArray 中每个元素的 Property.Value 类型时遇到了麻烦......

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

我之前的问题和这个问题的区别在于,我的外部 Linq 查询同时获取了 JObject 和 JArray 标记,所以这就是为什么我在第 40 行有一个 if (jo is JObject),在第 48 行有一个 if (jo is JArray)

一旦我知道我有一个 <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 类型的属性值感兴趣。

所以我尝试了:

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

编译器用错误CS0119 'JToken.Value&lt;T&gt;(object)' is a method, which is not valid in the given context.对Value属性加红线

我意识到 Linq express 中的“p”不是 JProperty。我猜这是一个JObject。而且我不知道如何转换“p”以便我可以检查它所代表的 JProperty 对象的类型。

最终,我需要用于 JArray 处理的代码(从第 48 行开始)添加一个返回 JObject,其中包含一个 JSON 数组,该数组仅由 JTokenType.String 类型的 JProperty 对象组成。这意味着给定示例 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"" }

这可能是我在这个“系列”中的最后一道关卡。

衷心感谢任何能够提供帮助并且愿意提供帮助的人,并再次特别感谢用户“Brian Rogers”和“dbc”,感谢他们真正令人惊叹的 JSON.NET/Linq 知识。

【问题讨论】:

  • 你是否尝试过制作 p.Value.获取类型()?
  • 是的,但这没有帮助。关键是使用 SelectMany() 并将 Where() 添加到该结果集的末尾。

标签: c# linq json.net


【解决方案1】:

这会产生您需要的输出:

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 =&gt; o.Properties() 是将JObject o 映射到其属性集合的lambda expressionp =&gt; p.Value.Type == JTokenType.String 是将属性p(由前面的SelectMany 子句生成)映射到一个真/假值的另一个lambda,该值指示是否该属性有一个字符串值。 op 都是 implicitly typed 的 lambda 输入参数。

此外,在// No help needed in this section 部分,其父对象为数组的对象将被跳过,因为它们将被(jo is JArray) 子句收集。

请注意,如果 jo 数组的不同子元素恰好具有相同的属性名称,JObject 构造函数可能会抛出重复键异常。

分叉fiddle

【讨论】:

  • 太棒了,dbc。那么说 o 指的是 JObject(来自 Children())而 p 指的是 JProperty(来自 o.Properties())是否正确?如果是,那是我的“缺少的 linq”。 :)
  • 在您的分叉小提琴结果中(运行后),我看到空查询项。我怎样才能跳过它们?我会在 Linq 表达式中添加一个 any() 修饰符吗?如果有,在哪里?
  • @Jazimov - 好的,看起来你确实需要No help needed 部分的帮助。我建议跳过所有父对象为数组的对象,因为它们将在随后的if (jo is JArray) 子句中收集。
  • 好吧,平心而论,我没有运行 No help needed 部分中的代码,它实际上会根据需要跳过(它还处理其他非数组 JSON,但未列出,因为它与这篇文章中的问题无关)。为后续问题道歉。 :) 我可以要求你对我的第一个评论发表最后评论吗?那我就上路了。再次感谢您!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多