如何通过Linq将一个组的结果合并为XML查询

本文关键字:结果 合并 查询 XML 一个 Linq 何通过 | 更新日期: 2023-09-27 18:23:59

我正在尝试构建一个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.Remove()的调用导致清空父元素,我也希望删除group.item斯中元素的父元素。所以,我试着用以下内容替换这一行:

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

这样做的问题是,此循环结果的连续迭代可能会导致NullReferenceException,因为父元素可能作为原始查询结果中的另一个组中的项存在!这当然会导致删除。父级为null,因为它以前已从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();

如何通过Linq将一个组的结果合并为XML查询

事实证明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

这导致查询只返回属于不同父级的元素分组。从正确的角度来看,我的代码所针对的XML文件返回了超过200000个分组。添加了"where"子句后,分组数量降至约150个!最后的结果是一样的。

也许可以删除做这件事的父母(以及孩子)?

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