如何通过 Linq-to-XML 查询合并一组结果?

How can I merge the results of a group by Linq-to-XML query?

我正在尝试构建执行以下步骤的 Linq-to-XML 查询:

  1. 将 XDocument 中的所有后代节点分组
  2. 聚合每个组中的不同项目
  3. 按照我选择的顺序用聚合项替换每个组中最后一项的父项中的元素
  4. 删除每个组中的所有原始项目

到目前为止,我已经完成了使用以下代码的前两个步骤。请注意,MyGroupByKeyFunction 的编写方式保证(除其他事项外)每个组中的所有元素都具有相同的深度(这就是 orderby 起作用的原因)。

var groups =
    from e in doc.Root.Descendants()
    group e by MyGroupByKeyFunction(e) into g
    orderby g.First().Ancestors().Count() descending
    select new {
        agg = g.Aggregate(new List<XElement>(), (list, el) => {
            list.Add(el);
            return list;
        }).Distinct(new MyCustomXElementEqualityComparer()),
        items = g,
        target = g.Last().Parent
    };

最后两步是我卡住的地方。我尝试了以下方法,但效果不尽如人意。

foreach (var group in groups)
{
    group.items.Remove();
    foreach (var item in group.merge)
    {
        group.target.Add(item);
    }
 }

group.items 中的元素已成功删除并填充目标,但我还希望在调用 group.items 时删除 group.items 中元素的父元素。 Remove() 导致父对象被清空。所以,我尝试用以下内容替换该行:

foreach (var delete in group.items)
{
    if (delete.Parent.Elements().Count() == 1)
        delete.Parent.Remove();

    else
        delete.Remove();
}

问题在于此循环结果的连续迭代可能会导致 NullReferenceException,因为父元素可能作为原始查询结果中另一组中的项目存在!这当然会导致 delete.Parent 为空,因为它之前已从 XML 树中分离出来。

我怎样才能解决这个问题?

更新

根据 Falanor 的建议,我已尝试将代码修改为以下内容。但是,这会导致 XDocument 的最终结果只包含根元素。我不明白为什么会这样。对此问题有什么想法或更好的解决方案吗?

HashSet<XElement> removed = new HashSet<XElement>();
foreach (var group in groups)
{
    removed.UnionWith(group.items.Select(el => el.Parent).Where(el => !el.Parent.Equals(group.target)));
    group.items.Remove();
    foreach (var item in group.merge)
    {
        if (!removed.Contains(item))
            group.target.Add(item);
    }
}

removed.Where(el => el.Parent != null).Remove();

也许删除 parents(以及 children)这样做?

foreach (var group in groups)
{
if(group.Parent.Elements().Count() == 1)
group.Parent.Remove();
else
group.items.Remove();
foreach (var item in group.merge)
{
    group.target.Add(item);
}
}

事实证明 Falanor 的想法是正确的,我只是在编写解决方案的方式上有一个小错误导致它无法运行。对 UnionWith 的方法调用应该是:

removed.UnionWith(group.items.Select(el => el.Parent).Where(el => !el.Equals(group.target)));

请注意错误出现在 where 子句中。

此外,对于任何感兴趣的人,我意识到我可以通过将以下 'where' 子句添加到我的初始查询(就在最终 'select' 语句之前)来显着减少代码的执行时间:

where g.Select(p => p.Parent).Distinct().Count() > 1

这会导致查询仅 return 属于不同父项的元素分组。为了正确看待事情,XML 文件我针对我的代码 returned 超过 200,000 个分组。有了额外的 'where' 子句,分组的数量下降到大约 150!和最后的结果一样。