53

I believe it's possible but couldn't figure out the syntax. Something like this:

xmlNode.SelectNodes("//*[count(child::*) <= 1]")

but this is not correct.

newman
  • 6,841
  • 21
  • 79
  • 126

4 Answers4

73

Use:

//node()[not(node())]

In case only element leaf nodes are wanted (and this needs clarification -- are elements that have non-element children considered leaf nodes?), then the following XPath expression selects them:

//*[not(*)]

Both expressions above are probably the shortest that select the desired nodes (either any-node or element -- leaf nodes).

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Can you explain why this works? I've looked over the XPath syntax and some tutorials, but I can't quite understand why this works. – rrs Aug 12 '14 at 15:18
  • @rrs: The first expression selects any node in the XML document that doesn't have any children -- this is what a leaf node is -- by definition. The second does something similar, but it selects any element that doesn't have a child - element. – Dimitre Novatchev Aug 12 '14 at 16:21
  • I understand what it does, but not how it does it. Why/how does `not(*)` select leaf nodes/elements? – rrs Aug 12 '14 at 16:34
  • 6
    `not(*)` means "does not have any element child" as "* selects all element children of the context node" as per the W3C XPath 1.0 recommendation: http://www.w3.org/TR/xpath/#path-abbrev (second bullet). This is a very short explanation, to go in depth, one needs a more or less full course in XPath. May I shamelessly recommend the second module of my Pluralsight training course on "XSLT 2.0 and 1.0 foundations"? The title of this course is "A Crash Course in XPath" and it is 70 minutes long: http://www.pluralsight.com/training/Courses/TableOfContents/xslt-foundations-part1 – Dimitre Novatchev Aug 12 '14 at 16:54
31

Any elements with no element child

//*[not(child::*)]
kevpie
  • 25,206
  • 2
  • 24
  • 28
  • 3
    +1 Right answer. But it means: *any elements with no element child*. So, it will select elements with text node child, empty elements, elements with mixed content (text nodes, PI, comments) –  Oct 13 '10 at 19:30
  • 1
    You probably meant: "Elements that don't have element-children" -- not "Elements with no children". It would be good to acknowledge this and to correct the text of your otherwise good answer. – Dimitre Novatchev Oct 13 '10 at 22:04
  • @Dimitre Thanks for holding my hand. I am SO n00b. – kevpie Oct 13 '10 at 22:07
  • what is the difference with @DimitreNovatchev 's `//*[not(*)]`? – lajarre Mar 26 '13 at 17:51
  • @lajarre, This is equivalent to the second expression in my answer -- only longer. – Dimitre Novatchev Mar 26 '13 at 18:27
2

Why less or equal to 1 ?

xmlNode.SelectNodes("//*[count(child::*) = 0]")

Make tests etc at this site http://www.whitebeam.org/library/guide/TechNotes/xpathtestbed.rhtm

Pretty helpful ..

Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
  • Thanks very much. This works great. So, it's more VB style equal. I thought it should be c-style because functions are case-sensitive. Why <= 1? I was confused by ChildNodes.Count which return 1 for x, but returns 0 for . – newman Oct 13 '10 at 19:20
  • and @miliu: the count test is not needed. Check @kevpie answer. –  Oct 13 '10 at 19:33
0

I'm adding this XSLT answer since it seems google's front matches lack such a solution:

After a long struggle with extracting CDATA as XML, eventually, this expression worked best for me:

<xsl:template match="*[not(child::*)]/text()">
IC4
  • 3
  • 2