Best LINQ-to-XML query for selecting nodes based on properties of descendant nodes?

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 am trying to select all “Parents” with a child who has at least two children (“grandson”). Please note: I am not looking for “parents” with at least two “grandchildren [ren]”.

The following LINQ query is being executed, but I feel that it is 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 indicate this?

+3
source share
3 answers

Ahh editing (2 grand-children) helps; -p

While XDocument is useful, from time to time I skip XPath / XQuery. With XmlDocument you can simply use doc.DocumentElement.SelectNodes("Parent[Child/Grandchild[2]]") .

+2
source

Hmmm ... I find it difficult to understand what exactly :)

Usually, to find out if there are any elements, I would use Any - but you want to see if there are at least two elements. We still do not need to use Count , although - because there are at least two elements - this is the same as skipping the element and see if there are any more. So that...

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

I think it works - and in fact it is not too poorly read:

For each parent, see if any of the children have any (great) children after ignoring the first (big) child.

I suspect using XPath (according to Marc's answer) would be the most readable option.

+5
source

I don't know the syntax like SQL to ensure that I get the syntax correctly if I write it this way, but you want to use .Any() instead of .Count() , and if you choose differently, you won't need Distinct() at the end. Try the following:

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

EDIT: If you want to make sure there are at least 2, you pretty much need to use .Count() .

0
source

Source: https://habr.com/ru/post/898117/