【问题标题】:JSON.NET: Flattening a JArray to JObjects within Linq QueryJSON.NET:使用 Linq 查询将 JArray 展平为 JObject
【发布时间】:2016-09-23 16:16:55
【问题描述】:

这是我之前的帖子的后续,这里: Querying and Filtering Array of JObjects with Linq

我在这里发布了一个 dotnetfiddle: https://dotnetfiddle.net/s75wGu

抱歉,但我无法弄清楚为什么我在 dotnetfiddle 中的 System.Collections.Generic.IEnumerable 上出现错误,但该程序在 Linqpad 和 Visual Studio 下运行。

源 JSON 如下所示:

{
  "Object": {
    "NM": "Name1",
    "AA": "Val01",
    "BB": "Val02",
    "CC": "Val03",
    "DD": "Val04",
    "EE": "Val05",
    "FF": "Val06",
    "GG": "Val07",
    "HH": "Val08",
    "Object": [
      {
        "NM": "Name2",
        "AA": "Val01",
        "BB": "Val02",
        "CC": "Val03",
        "DD": "Val04",
        "EE": "Val05",
        "FF": "Val06",
        "GG": "Val07",
        "HH": "Val08",
        "Object": [
          {
            "NM": "Name3",
            "AA": "Val01",
            "BB": "Val02",
            "CC": "Val03",
            "DD": "Val04",
            "EE": "Val05",
            "FF": "Val06",
            "GG": "Val07",
            "HH": "Val08"
          },
          {
            "NM": "Name4",
            "AA": "Val01",
            "BB": "Val02",
            "CC": "Val03",
            "DD": "Val04",
            "EE": "Val05",
            "FF": "Val06",
            "GG": "Val07",
            "HH": "Val08"
          },
          {
            "NM": "Name5",
            "AA": "Val01",
            "BB": "Val02",
            "CC": "Val03",
            "DD": "Val04",
            "EE": "Val05",
            "FF": "Val06",
            "GG": "Val07",
            "HH": "Val08"
          }
        ]
      }
    ]
  }
}

如您所见,该代码旨在查询 JSON 文档并将其展平为 List<JObject> 对象。

if (jo is JObject) 子句应该很简单:如果 Linq 迭代找到一个 JObject 对象,它会创建一个 joWork JObject 实例,为其添加属性,并返回单个 joWork 对象。这很好用。

但我的问题是使用 if (jo is JArray) 子句:在这里,我认为 Linq 希望我返回单个 JObject 对象,但我有一个 JObjects 数组,我想作为单独的 JObjects 返回。

我的 dotnetfiddle 代码产生的输出是这样的:

joWork-Object: {
  "_Parent": "",
  "NM": "Name1",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}
joWork-Array: {
  "_Parent": "Name1",
  "_Group": 1,
  "NM": "Name2",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}
joWork: 
joWork-Array: {
  "_Parent": "Name2",
  "_Group": 2,
  "NM": "Name5",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}
joWork: 
joWork: 
joWork: 

请注意,我们只有对象名称 (NM) Name1、Name2 和 Name5。我想要的是 Name1、Name2、Name3、Name4 和 Name5——每个都有一个 _Parent 属性,其中包含父对象的 NM 属性。

我意识到我只得到了 Name5,因为我的 Linq 查询中的 JArray if 语句只返回数组中的最后一个元素。我想返回数组中的所有元素。最终输出应如下所示:

joWork-Object: {
  "_Parent": "",
  "NM": "Name1",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}
joWork-Array: {
  "_Parent": "Name1",
  "_Group": 1,
  "NM": "Name2",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}
joWork-Array: {
  "_Parent": "Name2",
  "_Group": 2,
  "NM": "Name3",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}
joWork-Array: {
  "_Parent": "Name2",
  "_Group": 2,
  "NM": "Name4",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}
joWork-Array: {
  "_Parent": "Name2",
  "_Group": 2,
  "NM": "Name5",
  "AA": "Val01",
  "BB": "Val02",
  "CC": "Val03",
  "DD": "Val04",
  "EE": "Val05",
  "FF": "Val06",
  "GG": "Val07",
  "HH": "Val08"
}

【问题讨论】:

  • 你的问题有点不清楚。你能edit你的问题创建一个完整的minimal reproducible example你的问题吗?目前您的代码无法编译。此外,显示所需的完整输出对您很有帮助,而不仅仅是 sn-ps。你确定第二个 JSON sn-p 不应该是 { "Name" : "Array1B", .. 吗?
  • 确认一下,您想在访问"Array1A"数组时返回两个相同的JObject
  • 抱歉,你是对的。我将创建一个运行示例并解释我需要什么。我应该尽快发布。谢谢。
  • 好的,问题已修改。我希望这次我更清楚了。我觉得我已经完成了 98% 的工作——迫不及待地想看到解决方案!

标签: linq json.net


【解决方案1】:

您可以使用 Enumerable.SelectMany() 代替 Enumerable.Select() 在 linq 查询中返回多个对象,将返回的内部枚举扁平化为其构成对象以进行整体枚举:

        var root = (JContainer)JToken.Parse(jsonFromFile);

        var controlRoot = (JContainer)root["Object"];

        string parentKey = "";
        int groupId = 0;

        var query =
            controlRoot
            .DescendantsAndSelf()
            .Where(jt => (jt.Type == JTokenType.Object) || (jt.Type == JTokenType.Array))
            .SelectMany(jo =>
            {
                if (jo.Parent is JProperty)
                {
                    var ParentName = ((JProperty)jo.Parent).Ancestors()
                                    .Where(jt => jt.Type == JTokenType.Property)
                                    .Select(jt => ((JProperty)jt).Name.ToString())
                                    .FirstOrDefault();

                    if (ParentName == "Object")
                    {
                        parentKey = ((JProperty)jo.Parent).AncestorsAndSelf()   // Climb up the json container parent/child hierachy
                           .Select(p => p.SelectToken("NM"))                    // Get the "parentKey" property in the current parent (if present)
                           .FirstOrDefault(k => k != null).ToString();          // Return the first one found.
                    }

                    if (jo is JObject)
                    {
                        // add a property for the parent
                        var joWork = new JObject(new JProperty("_Parent", parentKey));

                        // add only the string properties in the current object
                        joWork.Add(((JObject)jo).Properties()
                            .Where(p => p.Value.Type == JTokenType.String));

                        return new[] { joWork };
                    }
                    else if (jo is JArray)
                    {
                        groupId++;
                        return from o in jo
                               let arrayItems = ((JObject)o).Properties().Where(p => p.Value.Type == JTokenType.String).ToList()
                               where arrayItems.Count > 0
                               select new JObject(new[]
                                   {
                                       new JProperty("_Parent", parentKey),
                                       new JProperty("_Group", groupId),
                                   }
                                   .Concat(arrayItems));
                    }
                }
                return Enumerable.Empty<JObject>();
            }
            )
            .ToList();

        Console.WriteLine(JsonConvert.SerializeObject(query, Formatting.Indented));

这里我在SelectMany() 方法中使用LINQ 查询语法,因为它更紧凑。替代方法语法是:

                    else if (jo is JArray)
                    {
                        groupId++;
                        return jo.Select(o =>
                            {
                                var arrayItems = ((JObject)o).Properties()
                                    .Where(p => p.Value.Type == JTokenType.String).ToList();
                                if (arrayItems.Count > 0)
                                {
                                    var joWork = new JObject();

                                    // add a property for the parent
                                    joWork.Add(new JProperty("_Parent", parentKey));
                                    joWork.Add(new JProperty("_Group", groupId));

                                    // add only the string properties in the current object
                                    joWork.Add(arrayItems);
                                    return joWork;
                                }
                                else
                                {
                                    return null;
                                }
                            })
                            .Where(o => o != null);
                    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-20
    • 1970-01-01
    相关资源
    最近更新 更多