繁体   English   中英

如何通过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.Remove()的调用导致清空父元素的情况下,删除group.items中元素的父元素。 因此,我尝试用以下命令替换该行:

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

    else
        delete.Remove();
}

问题在于,此循环结果的连续迭代会导致NullReferenceException,因为父元素可能作为项目存在于原始查询结果的另一个组中! 当然,这会导致delete.Parent为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();

事实证明,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文件返回了200,000多个分组。 加上附加的“ 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);
}
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM