XPath child::* vs child::node()
Asked Answered
D

2

7

I'm working with an XSLT transformation, and I found an interesting question that I couldn't answer:

What's the difference between child::* and child::node() ?

I want to create a condition in which I delimit the quantity of children elements to 1, in this case:

<xsl:if test="parent[count(child::*) eq 1])"> 

vs

<xsl:if test="parent[count(child::node()) eq 1])"> 

What would be the difference?

Doff answered 8/9, 2017 at 17:11 Comment(3)
The spec is online so try reading w3.org/TR/xpath20/#node-tests and tell us which parts you don't understand.Willis
Excellent question! There are subtle differences that escape many casual users of XPath. See my answer below for details.Mammy
Where did you look for an answer to this question before asking here?Matted
M
8

To understand the difference between child::* and child::node() in XPath, understand not only the difference between the * and node() node tests, but also the concept of the principal node type of an axis...

Principal Node Type

Rule: If an axis can contain elements, then its principal node type is element; otherwise, it's the node type that the axis can contain. (For example, the principal node type of attribute axis is attribute because it can only contain attributes.)

The child axis can contain elements, so the principal node type of the child axis is element.

Node Tests per Axis

Therefore, the difference between child::* and child::node() is that

  • the * node test on the child axis succeeds for all child elements of the context node, because the * node test succeeds for all nodes of the principal node type (element, here) whereas
  • the node() node test succeeds for all child nodes of the context node, because the node() node test succeeds for all nodes types. However, note that not all nodes types can be on the child axis. Here are the seven types of nodes and whether they can appear on the child axis:
    • root: No, the root is the child of no other node, by definition.
    • element: Yes
    • text: Yes
    • attribute: No, attributes have their own axis.
    • namespace: No, namespaces have their own axis.
    • processing instruction: Yes
    • comment: Yes

Therefore, child::* matches all element children of the context node, and child::node() matches all all element, text, and processing instruction children of the context node.

Mammy answered 8/9, 2017 at 19:36 Comment(1)
Good answer. This difference is also documented by W3 Schools.Conventioneer
C
3

What's the difference between child::* and child::node().

To quote from here:

  • * matches any element
  • node() matches any node other than an attribute node and the root node

So child::* matches only child elements and child::node() matches any child nodes except attributes and the root node. These can be five of the seven node types: elements, comments, text and namespace nodes and processing instructions (see here).

Cushy answered 8/9, 2017 at 17:26 Comment(3)
Since when is CDATA a node type?! And since when is w3schools a reference?! There are exactly seven types of node listed in the XPath specification: root, element, text, attribute, namespace, processing instruction and comment.Condenser
@michael.hor257k: Thanks for correcting me. I was mistaken with the CDATA by some past coding experiences and didn't had it in mind correctly.Cushy
This is basically correct (+1), but the full answer is a little more subtle than can fit in a comment, so I've added a new answer to explain. (It's not that * as a node test can only match elements, and it's not that node() as a node test cannot match attributes; it's their position on the child axis that implicitly adds these restrictions. See my answer for further details.)Mammy

© 2022 - 2024 — McMap. All rights reserved.