23

I just wrote an XSLT that did not work at first.

I had to rename all children of <Recordset> to <C>:

<?xml version="1.0" encoding="utf-8"?>
<Record>
<Recordset>
    <company>102</company>
    <store>1801</store>
    ....
</Recordset>
<Recordset>
....
</Recordset>
</Record>

I used the following XSLT:

<xsl:template match="Record/Recordset/child::*">    
    <xsl:element name="C">
        <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
</xsl:template>

It works and renames all children of <Recordset> to <C>. But first my match in the template looked like this:

<xsl:template match="Record/Recordset/child::node()">

My idea was that every child of <Recordset> is a node, thus node() would be appropriate. It worked too but it inserted an extra <C/> for each child.

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

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Peter
  • 1,786
  • 4
  • 21
  • 40
  • _"My idea was that every child of is a node"_ >> this statement is correct, but you forgot about the text node children, even though they are whitespace-only in your example. Other children that are not elements are also nodes: comments and processing instructions. Namespace nodes and attribute nodes are treated specially. – Abel Oct 07 '15 at 14:51
  • Thank you Abel for your comment. I have figured it out in the meantime. – Peter Oct 09 '15 at 09:34

2 Answers2

41

child::node() matches any node that's not an attribute node, namespace node, or document node. That means that it does match processing instructions, comments, and text nodes.

child::* matches only elements.

See section 5.5.3 of the spec:

The pattern node() matches all nodes selected by the expression root(.)//(child-or-top::node()), that is, all element, text, comment, and processing instruction nodes, whether or not they have a parent. It does not match attribute or namespace nodes because the expression does not select nodes using the attribute or namespace axes. It does not match document nodes because for backwards compatibility reasons the child-or-top axis does not match a document node.

Update: Michael's answer inspired the following stylesheet. Use it to test the types of nodes as they're processed:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/|node()">
        <xsl:call-template name="type" />
        <xsl:text>  [  </xsl:text>
        <xsl:value-of select="." />
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="node()" />
        <xsl:text>  ]  </xsl:text>
    </xsl:template>
    <xsl:template name="type">
        <xsl:choose>
            <xsl:when test="count(.|/)=1">
                <xsl:text>Root</xsl:text>
            </xsl:when>
            <xsl:when test="self::*">
                <xsl:text>Element </xsl:text>
                <xsl:value-of select="name()" />
            </xsl:when>
            <xsl:when test="self::text()">
                <xsl:text>Text</xsl:text>
            </xsl:when>
            <xsl:when test="self::comment()">
                <xsl:text>Comment</xsl:text>
            </xsl:when>
            <xsl:when test="self::processing-instruction()">
                <xsl:text>PI</xsl:text>
            </xsl:when>
            <xsl:when test="count(.|../@*)=count(../@*)">
                <xsl:text>Attribute</xsl:text>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Modify what's matched/selected to test other patterns. For example, the following input:

<A attr="test" other="val">
  <B/>
  <C>some value</C>
  <!-- a comment -->
  <D/>
</A>

Produces the following output:

Root  [  

  some value



Element A  [  

  some value



Text  [  

  ]  Element B  [  
  ]  Text  [  

  ]  Element C  [  some value
Text  [  some value
  ]    ]  Text  [  

  ]  Comment  [   a comment 
  ]  Text  [  

  ]  Element D  [  
  ]  Text  [  

  ]    ]    ]  

Special thanks to this page for getting me started on the node-type tests. (It's especially fitting that one of Michael's answers from over six years ago appears there, too.)

Wayne
  • 59,728
  • 15
  • 131
  • 126
  • Thank you very much for the node-test example! I have always been a bit confused about text, node and element... – Peter Mar 24 '11 at 09:23
20

To expand on lwburk's answer, if your XML looks like this:

<A>
  <B/>
  <C/>
  <D/>
</A>

Then the A element has 7 child nodes; three of them are elements, four are text nodes. The expression child::node() matches all 7, whereas child::* only matches the elements.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Thank you Michael. I will have to play around with that too. I don't "see" the 4 text nodes yet. – Peter Mar 24 '11 at 09:25
  • 5
    After every `">"` and before the next `"<"` there is some whitespace. There are four such gaps, and each one is a whitespace-only text node. – Michael Kay Mar 29 '11 at 13:00
  • Hello Michael, Thank you - I didn't know about the whitespaces being text nodes. Now I get it and now I also get my original problem with the extra -elements that were inserted. Thank you very much! – Peter Apr 01 '11 at 14:23