views:

31

answers:

1

When I use "apply-templates" and select a variable sequence, does the template act upon the context of the element in the sequence, or does it act upon the context of the element in the document?

In the below example, it seems to do either, or none, but I don't understand why.

<root>
<a/>
<b/>
<c><a/></c>
<a/>
</root>

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"&gt;
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="root">
    <!--this variable give me a sequence of "a" elements-->
    <xsl:variable name="someElementA1" select="//a"/>
    <xsl:variable name="someElementA2">
        <xsl:copy-of select="//a"/>
    </xsl:variable>
    <xsl:for-each select="$someElementA1/a">
        <xsl:element name="test">
            <xsl:text>This is scenario 1: </xsl:text>
            <xsl:apply-templates select="."/>
        </xsl:element>
    </xsl:for-each>
    <xsl:for-each select="$someElementA2/a">
        <xsl:element name="test">
            <xsl:text>This is scenario 2: </xsl:text>
            <xsl:apply-templates select="."/>
        </xsl:element>
    </xsl:for-each>
    <xsl:element name="test">
        <xsl:text>This is scenario 3: </xsl:text>
        <xsl:apply-templates select="$someElementA1"/>
    </xsl:element>
    <xsl:element name="test">
        <xsl:text>This is scenario 4: </xsl:text>
        <xsl:apply-templates select="$someElementA2/a"/>
    </xsl:element>
</xsl:template>
<!--these are the templates to apply--> 
<xsl:template match="a[parent::c]">
    <xsl:text>This element has a parent</xsl:text>
</xsl:template>
<xsl:template match="a[parent::root]">
    <xsl:text>This element is in the root element</xsl:text>
</xsl:template>
<xsl:template match="a">
    <xsl:text>This element is in the sequence</xsl:text>
</xsl:template>
</xsl:stylesheet>

OUTPUT:

<test>This is scenario 2: This element is in the sequence</test>
<test>This is scenario 2: This element is in the sequence</test>
<test>This is scenario 2: This element is in the sequence</test>
<test>This is scenario 3: This element is in the root elementThis element has a parentThis element is in the root element</test>
<test>This is scenario 4: This element is in the sequenceThis element is in the sequenceThis element is in the sequence</test>
+1  A: 

The results you get are due to the following facts:

  1. A sequence of nodes contains the nodes as they are in their respective document -- not copies of the nodes.

  2. <xsl:copy-of> creates a copy of each node selected by the expression in the select attribute.

creates a new XML fragment (temporary tree) and this is the value of the variable $someElementA2

$someElementA1/a

does not select any node, because none of the elements in $someElementA1 have any children named a.

$someElementA2/a

selects all elements a that are children of the document node (/).

Dimitre Novatchev
I researched a little further... instead of using copy-of use xsl:sequence. But that doesn't seem to work unless you also put an as="element()*" to the variable. The aha moment is that there is a difference between a collection of elements and I guess what you would call a sequence of elements, which is more like navigating a path of elements and acting upon them in thier document context.
Darin Walsh
@Darin-Walsh: Yes, if there are elements generated inside the body of a variable, it has a default type `document-node()`. But why should you do this at all? Better use a direct definition: `<xsl:variable name="vsomeName" select="someExpression"/>`Just remember -- try never to put something in the body of a `<xsl:variable>`. Try always to specify the value of a variable in its `select` attribute.
Dimitre Novatchev
The reason I put it in the body of the variable is because I am either using complex conditionals, or adding elements together in a sequence. I will see if the variable can handle this "mixed" content of actual document nodes and generated elements, and what happens to the sequence when it is passed through a param. <xsl:variable name="cells"> <xsl:copy-of select="$cells"/> <xsl:element name="cell"> <xsl:apply-templates select="desc"/> </xsl:element> </xsl:variable>There might be a way to do the above with a select statement using "for..." ?
Darin Walsh
@Darin-Walsh: This will raise an error: `<xsl:variable name="cells"> <xsl:copy-of select="$cells"/>` because inside the body of the variable you're referencing the *same* variable -- but this variable is not yet defined...
Dimitre Novatchev