Search code examples
xsltxpathxslt-1.0xmlnodeapply-templates

what is the explicit version of node()


Is the well-known XSLT 1.0 identity template

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

synonymous with

<xsl:template match="/|@*|*|processing-instruction()|comment()|text()">
  <xsl:copy>
    <xsl:apply-templates select="@*|*|processing-instruction()|comment()|text()"/>
  </xsl:copy>
</xsl:template>

i.e. is it correct that node() includes / in the match statement and doesn't include / in the select statement?


Solution

  • The node() node-test doesn't have different behavior depending on whether it's in a match or a select attribute. The expanded version of the identity template is the following:

    <xsl:template match="@*|*|processing-instruction()|comment()|text()">
      <xsl:copy>
        <xsl:apply-templates select="@*|*|processing-instruction()|comment()|text()"/>
      </xsl:copy>
    </xsl:template>
    

    The node() node-test matches any node, but when it is not given an explicit axis, it is on the child:: axis by default. So the pattern match="node()" does not match the document root or attributes, because they are not on the child axis of any node.

    You can observe that the identity template doesn't match the root node because this has no output:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="text" indent="yes"/>
    
        <xsl:template match="@* | node()">
          <xsl:if test="count(. | /) = 1">
            <xsl:text>Root Matched!</xsl:text>
          </xsl:if>
        </xsl:template>
    </xsl:stylesheet>
    

    and this outputs "Root Matched!":

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="text" indent="yes"/>
    
        <xsl:template match="@* | node() | /">
          <xsl:if test="count(. | /) = 1">
            <xsl:text>Root Matched!</xsl:text>
          </xsl:if>
        </xsl:template>
    </xsl:stylesheet>
    

    You can verify that the node() test applies to the root node and attributes by running this on any document that has attributes:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="text" indent="yes"/>
    
      <xsl:template match="node()">
        <xsl:apply-templates select="@* | node()" />
      </xsl:template>
    
      <xsl:template match="/">
        <xsl:if test="self::node()">
          node() matches the root!
        </xsl:if>
        <xsl:apply-templates select="@* | node()" />
      </xsl:template>
    
      <xsl:template match="@*">
        <xsl:if test="self::node()">
          node() matches an attribute!
        </xsl:if>
      </xsl:template>
    </xsl:stylesheet>
    

    And here is another way to observe that the node() test applies to the root node:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="text" indent="yes"/>
    
      <xsl:template match="/*">
        <xsl:value-of select="concat('The root element has ', count(ancestor::node()), 
                                     ' ancestor node, which is the root node.')"/>
      </xsl:template>
    </xsl:stylesheet>