Node tests

1. What does node() match?
2. testing the type of a node
3. How to test if the current node is an attribute or a namespace node
4. How to test a node type
5. node() clarification, node-test or pattern
6. node()
7. Identify node type
8. How to find the longest node in a node-set
9. Comparing node for identity using union

1.

What does node() match?

Jeni Tennison



>> Use <xsl:copy-of> and select all the children of the <mytag>
>> element using the node() node test (which matches any node,
>> including elements and attributes):
>> 
>>   <xsl:copy-of select="mytag/node()" />
>> 
>> <xsl:copy-of> gives you an exact copy of the nodes.
>
> uh oh ... i'm confused again.  i thought "node()" explicitly
> did *not* match attribute nodes, but only

The node test "node()" matches attribute nodes, but the child axis can only select the node types that you mention:


> 1) elements
> 2) text
> 3) comments
> 4) processing instructions

When you do:

  <xsl:copy-of select="mytag/node()" />

this is expanded to:

  <xsl:copy-of select="child::mytag/child::node()" />

and you get the child nodes (which cannot include attributes since attributes aren't children) of the child <mytag> element of the context node.

You can tell that the node() node test matches attributes because the expression ".", which expands to the expression "self::node()", can be used to select attributes. Unlike the child axis, the self axis selects the context node no matter what its type.

2.

testing the type of a node

Jeni Tennison


>I needed to check if the current node is an element:
	

The easiest way to test whether the current node is an element is with:

 self::*

This selects the current node if it is an element (with any name); if a node is selected through this path, then the resultant node set evaluates as boolean true.

(Similarly, you can check whether the current node is a comment with self::comment(), a text node with self::text() and a processing instruction with self::processing-instruction(). It's only with attributes and namespace nodes that you can't use the self:: axis because they're only accessible through the attribute:: and namespace:: axes.)

3.

How to test if the current node is an attribute or a namespace node

Michael Kay


> Which is the simplest XPath expression to test whether the 
> current node is an attribute?
	

Not easy! I'd suggest:

count(. | ../@*) = count(../@*)
> 
> Or a namespace?
> 
count(. | ../namespace::*) = count(../namespace::*)

Miloslav Nic offers

Longer, but probably easier to understand:

generate-id() = generate-id(parent::*/@*[name()=name(current())])

4.

How to test a node type

Lassi A. Tuura

I want to see if

following-sibling::node()[1]

is a comment or not. is there way to test that?

<xsl:if test="following-sibling::node()[1][self::comment()]">
   Its a comment
   

Ednote: This is a node test, not a function().

Similarly, the node test processing-instruction() is true for any processing instruction. and text() for any text node, node() for any node()

Linda points out: Mentioned in chapter 6 of XSLT Programmer's reference (p 372), and defined in section 2.3 of XPath, and shown on the Quick reference card from Mulberrytech!

Tony Graham responds with the formal side:

Look under the heading "Node Tests [XPath 2.3]".

They look like functions but they're not. XPath section 3.2, Function Calls, has:

[16]  FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* ) ')'

but section 3.7, Lexical Structure, has:

[35]  FunctionName ::= QName - NodeType

and

[38]  NodeType     ::= 'comment'
                       | 'text'
                       | 'processing-instruction'
                       | 'node'

So the node type node tests look like function calls but they're not because their names are explicitly not allowed as function calls.

5.

node() clarification, node-test or pattern

Jeni Tennison



> DC wrote:
>>no node() does match attribute nodes.
>
> This is interesting - can you show an example, because I from that
> would read that I could drop the @* match like this:
>
> <xsl:template match="node()">
>   <xsl:copy>
>     <xsl:apply-templates select="@*|node()"/>
>   </xsl:copy>
> </xsl:template>
>
> Which you can't - what am I missing?

David C.'s being a little pedantic (now there's a surprise) and saying that the *node test* "node()" matches attribute nodes. He's not saying that the *pattern* "node()" matches attribute nodes.

The *pattern* "node()" as in:

<xsl:template match="node()">
  ...
</xsl:template>

is actually a shorthand for the pattern "child::node()", and matches any node that is a child of another node. Attributes aren't children of any other nodes, so they don't match this pattern.

However, if you use the pattern "attribute::node()" (or @node()) then that will match any node that is an attribute of another node, and will therefore match attributes. This is because the node test "node()" matches all nodes, including attributes.

The same goes for expressions: the expression "node()" is a shorthand for the expression "child::node()" and will select all child nodes of the context node. The expression "attribute::node()" or "@node()" will select all attributes of the context node; since only attributes are attributes of a node, it's equivalent to do "@*", which has the benefit of being shorter, so that's what we use most of the time.

6.

node()

Dimitre Novatchev



> According to the XSLT 1.0 spec
> (http://www.w3.org/TR/1999/REC-xslt-19991116),
> section 5.2 (http://www.w3.org/TR/1999/REC-xslt-19991116#patterns), node()
> matches "any node other than an attribute node and the root node".

This generally is not true. What will be matched by the node test

  node()

depends on the axis specifyier that comes with it:

- child::node() matches elements, PIs, comments and text nodes
- parent::node() matches elements or the root node.
- attribute::node() matches attributes
- namespace::node() matches namespaces.

So, depending on the axis, every possible type of node can be matched by the node() node-test.

7.

Identify node type

Ken Holman


>Is there an easy way (or any way at all) to identify the type of the 
>node being processed?

Yes, there is a way for all seven kinds of node.


   <xsl:choose>

     <xsl:when test="self::*">                  <!--an element-->
       <xsl:text>Element: </xsl:text><xsl:value-of select="."/>
     </xsl:when>
     <xsl:when test="self::text()">              <!--show text-->
       <xsl:text>Text: </xsl:text><xsl:value-of select="."/>
     </xsl:when>
     <xsl:when test="self::comment()">      <!--reveal comment-->
       <xsl:text>Comment: </xsl:text><xsl:value-of select="."/>
     </xsl:when>
     <xsl:when test="self::processing-instruction()">   <!--pi-->
       <xsl:text>PI: </xsl:text><xsl:value-of select="."/>
     </xsl:when>
     <xsl:when test="count(.|/)=1">                  <!--root-->
       <xsl:text>root </xsl:text>
     </xsl:when>
                                            <!--any attribute-->
     <xsl:when test="count(.|../@*)=count(../@*)">
       <xsl:text>attribute </xsl:text>
     </xsl:when>
                                       <!--specific namespace-->
     <xsl:when test="count(.|../namespace::xsl)=
                     count(../namespace::xsl)">
       <xsl:text>XSL namespace </xsl:text>
     </xsl:when>
                                            <!--any namespace-->
     <xsl:when test="count(.|../namespace::*)=
                     count(../namespace::*)">
       <xsl:text>namespace </xsl:text>
     </xsl:when>
   </xsl:choose>

8.

How to find the longest node in a node-set

Phil Lanch


> Does anyone know a way I could define a variable that would
> contain the number of characters in the longest node in a
> node-set? Let the node set in question be
> file://DIV[@type='Chapter']: if I have three, with string
> lengths 88888, 99999, and 111110, I want my variable to be
> 111110.
 

<xsl:template name="getlongest">
  <xsl:param name="nodeset"/>
  <xsl:param name="longest" select="0"/>
  <xsl:choose>
    <xsl:when test="$nodeset">
      <xsl:choose>
        <xsl:when 
             test="string-length($nodeset[1]) > $longest">
          <xsl:call-template name="getlongest">
            <xsl:with-param name="nodeset" 
                    select="$nodeset[position()
> 1]"/>
            <xsl:with-param name="longest"
select="string-length($nodeset[1])"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="getlongest">
            <xsl:with-param 
               name="nodeset" select="$nodeset[position()
> 1]"/>
            <xsl:with-param 
     name="longest" select="$longest"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$longest"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

            

9.

Comparing node for identity using union

Michael Kay




> I run to the debuger i see that

> (count(parent::Menu|$snode)=1) is allways true, even when $snode no 
> node, and even when there is no parent::Menu .

count(A|B)=1 will be true if A contains one node and B is empty, or if B contains one node and A is empty, or if both A and B contain a single node and this is the same node.

This is only a safe test for identity if you know that each of the node-sets A and B is a single node. If you're not sure of that, a safer test is

count(A)=1 and count(B)=1 and count(A|B)=1