基于后代节点属性选择节点的最佳LINQ-to-XML查询是什么?

3

I have the following XML document:

<?xml version="1.0" encoding="UTF-8"?>
<FamilyTree>
  <Parent name="Ken">
    <Child name="Lorna">
      <Grandchild name="Andrew"/>
      <Grandchild name="Brian"/>
    </Child>
    <Child name="Mike">
      <Grandchild name="Ann"/>
      <Grandchild name="Beth"/>
    </Child>
  </Parent>
  <Parent name="Norma">
    <Child name="Owen">
      <Grandchild name="Charles"/>
    </Child>
    <Child name="Peter">
      <Grandchild name="Charlotte"/>
    </Child>
  </Parent>
  <Parent name="Quinn">
    <Child name="Robert">
      <Grandchild name="Debbie"/>
      <Grandchild name="Eric"/>
    </Child>
    <Child name="Susan">
      <Grandchild name="Frank"/>
    </Child>
  </Parent>
  <Parent name="Tom">
    <Child name="Ursula">
      <Grandchild name="George"/>
      <Grandchild name="Harriet"/>
    </Child>
    <Child name="Victor">
      <Grandchild name="Ian"/>
      <Grandchild name="Juliet"/>
    </Child>
  </Parent>
</FamilyTree>

我正在尝试选择所有具有至少两个孩子(“Grandchild”)的孩子的“Parents”。请注意,我在寻找至少有两个“Grandchild[ren]” 的“Parents”。
以下LINQ查询有效,但我觉得它并不是最优雅的。
IEnumerable<XElement> parents = (from c in familyTreeElement.Descendants("Child")
                                 where c.Elements().Count() > 1
                                 select c.Parent).Distinct();

有没有更好的方法来指定这个?

第一条注释 - 这是检查至少有两个子元素... - Jon Skeet
1
谢谢。我已经修复了这个错字。我确实想要检查至少两个孩子。 - Gayle
3个回答

5

嗯...我发现很难确切理解它 :)

通常,如果要查找是否有任何元素,我会使用Any - 但是您想要查看是否至少有两个元素。尽管如此,我们仍不需要使用Count - 因为至少有两个元素与跳过一个元素并查看是否仍然有任何元素相同。所以...

var parents = familyTreeElement.Elements("Parent")
    .Where(parent => parent.Elements("Child").Any(
                     child => child.Elements("Grandchild").Skip(1).Any()));

我认为这个方法可行 - 实际上它并不读起来太糟糕:

对于每个父元素,查看是否有任何一个子元素在忽略第一个(孙)子元素后有任何(孙)子元素。

我怀疑使用XPath(如Marc的答案所示)可能是最易读的选项。


这是一种聪明的方法,可以在不计数的情况下找到是否有两个。+1。 - mqp
2
我之前没有想过,但这正是我们作为人类所做的。如果有人要求我们确保罐子里至少有十块饼干,我们只会看到前十块饼干 :) - Jon Skeet

2

啊,编辑(2个孙子)很有帮助;-p

虽然 XDocument 很有用,但有时我会想念 XPath/XQuery。使用 XmlDocument,你只需要使用 doc.DocumentElement.SelectNodes("Parent[Child/Grandchild[2]]")


那么不使用XPathExtensions的原因是什么呢? - Jon Skeet
是的,抱歉。打错字了。当我试图解释所有内容时,想得太认真了。 - Gayle
familyTreeElement.XPathSelectElements("Parent[Child/Grandchild[2]]"); 似乎可以工作。 - Gayle

0

我对"类似SQL"的语法不够了解,无法保证如果按照那种方式编写代码,语法会正确无误。但是你应该使用.Any()而不是.Count(),如果以不同的方式进行选择,就不需要在最后加上Distinct()。试试这样:

IEnumerable<XElement> parents =
    familyTreeElement.Elements("Parent").Where(
        parent => parent.Elements("Child").Any(
            child => child.Elements().Count() >= 2));

编辑:如果你想确保至少有2个,你基本上必须使用.Count()


1
Count() 是给懦夫用的 - 你可以不用它 ;) - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接