Question

I try to create a variable, which I can use in a later template:

<xsl:variable name="fc">
    <xsl:choose>
        <xsl:when test="self::node()='element1'">gray</xsl:when>
        <xsl:otherwise>red</xsl:otherwise>
    </xsl:choose>
</xsl:variable>

Unfortunately it does not work.

<xsl:template match="element1">
    <h1><font color="{$fc}"><xsl:value-of select="self::node()"/></font></h1>
</xsl:template>

What am I doing wrong?

Here is the extensive code:

XML:

<root 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.test.com scheme.xsd" xmlns="http://www.test.com" xmlns:tst="http://www.test.com">
 <elementA>
    <elementB tst:name="name">
        <elementC tst:name="name">
            <element1> Test1 </element1>
            <element2> Test2 </element2>
         </elementC >
    </elementB> 
  </elementA>    
</root>

All the elements are qualified and part of the namespace "http://www.test.com".

XSLT:

<xsl:template match="/">
<html>
<body><xsl:apply-templates select="tst:root/tst:elementA/tst:elementB/tst:elementC/tst:element1"/>
</body>
</html>

</xsl:template>
<xsl:variable name="var_fc">
<xsl:choose>
    <xsl:when test="local-name(.)='tst:element1'">gray</xsl:when>
    <xsl:otherwise>red</xsl:otherwise>
</xsl:choose>
</xsl:variable>

<xsl:template match="tst:element1">
<h2><font color="{$var_fc}"><xsl:value-of select="self::node()"/></font></h2>
</xsl:template>

As a result, element1 should turn gray, but it always turn red.

Was it helpful?

Solution

You can't use a variable for this, as the content of an xsl:variable is evaluated just once at definition time, whereas you want to evaluate some logic every time the variable is referenced, in the current context at the point of reference.

Instead you need a template, either a named one:

<xsl:template name="fc">
  <xsl:choose>
    <xsl:when test="local-name()='element1'">gray</xsl:when>
    <xsl:otherwise>red</xsl:otherwise>
  </xsl:choose>
</xsl:template>

or (better) a pair of matching templates with a mode, to let the template matcher do the work:

<!-- match any node whose local name is "element1" -->
<xsl:template mode="fc" match="node()[local-name() = 'element1']">gray</xsl:template>

<!-- match any other node -->
<xsl:template mode="fc" match="node()">red</xsl:template>

When you want to use this logic:

<h1>
  <font>
    <xsl:attribute name="color">
      <xsl:apply-templates select="." mode="fc" />
    </xsl:attribute>

Seeing as you have the tst prefix mapped in your stylesheet you could check the name directly instead of using the local-name() predicate:

<xsl:template mode="fc" match="tst:element1">gray</xsl:template>
<xsl:template mode="fc" match="node()">red</xsl:template>

OTHER TIPS

XSLT variables are designed not to be changeable. Actually they could be named constants. If your variable fc is created global, it will use the root element for choose. You have to use choose in the actual template to be tested against the current element. If you want to have "red" and "gray" defined only once, create two variables with just that text content and use these instead the plain text in the choose.

Maybe it is a typo:

<xsl:when test=self::node()='element1'">gray</xsl:when>

should be:

<xsl:when test="self::node()='element1'">gray</xsl:when>

there is a missing quote.

I think instead of test="self::node()='element1'" you want test="self::element1" or test="local-name(.) = 'element1'".

A couple of other errors in your code:

(1) self::node() = 'element1'

tests whether the content of the element is "element1", not whether its name is "element1"

(2) local-name(.)='tst:element1'

will never be true because the local name of a node never contains a colon.

Experienced users would often write this code using template rules:

<xsl:template mode="var_fc" match="tst:element1">gray</xsl:template>
<xsl:template mode="var_fc" match="*">red</xsl:template>

and then

<xsl:apply-templates select="." mode="var_fc"/>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top