问题
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>
I'm trying to select all the "Parents" with a Child who has at least two children ("Grandchild") of his/her own. Note that I'm not looking for "Parents" with at least two "Grandchild[ren]".
The following LINQ query works, but I've a feeling it's not the most elegant.
IEnumerable<XElement> parents = (from c in familyTreeElement.Descendants("Child")
where c.Elements().Count() > 1
select c.Parent).Distinct();
Is there a better way to specify this?
回答1:
Ahh the edit (2 grand-children) helps ;-p
While XDocument
is useful, at times I miss XPath/XQuery. With XmlDocument
you could just use doc.DocumentElement.SelectNodes("Parent[Child/Grandchild[2]]")
.
回答2:
Hmmm... I'm finding it hard to get my head round it exactly :)
Normally to find out if there are any elements, I'd use Any
- but you want to see if there are at least two elements. We still don't need to use Count
though - because there being at least two elements is the same as skipping an element and seeing if there are still any. So...
var parents = familyTreeElement.Elements("Parent")
.Where(parent => parent.Elements("Child").Any(
child => child.Elements("Grandchild").Skip(1).Any()));
I think that works - and actually it doesn't read too badly:
For each parent, see whether any of there children has any (grand)children after ignoring the first (grand)child.
I suspect using XPath (as per Marc's answer) would be the most readable option though.
回答3:
I don't know the "SQL-like" syntax enough to guarantee that I'll get the syntax right if I write it that way, but you want to use .Any()
instead of .Count()
, and if you select in a different manner, you don't need the Distinct()
at the end. Try this:
IEnumerable<XElement> parents =
familyTreeElement.Elements("Parent").Where(
parent => parent.Elements("Child").Any(
child => child.Elements().Count() >= 2));
EDIT: If you want to ensure that there are at least 2, you do pretty much have to use .Count()
.
来源:https://stackoverflow.com/questions/787240/best-linq-to-xml-query-to-select-nodes-based-on-properties-of-descendant-nodes