Question

Is it possible to create a node-set variable from an rtf using xsl:choose (for use in MSXML engine)?

I have the following construct:

<xsl:choose>
    <xsl:when test="function-available('msxsl:node-set')">
        <xsl:variable name="colorList" select="msxsl:node-set($std:colorList)"/>
        <xsl:for-each select="$colorList/color">
             tr.testid<xsl:value-of select="@testid"/> {
                color:<xsl:value-of select="."/>;
            }
       </xsl:for-each>
    </xsl:when>
    <xsl:otherwise>
        <xsl:variable name="colorList" select="$std:colorList"/>
        <xsl:for-each select="$colorList/color">
             tr.testid<xsl:value-of select="@testid"/> {
                color:<xsl:value-of select="."/>;
            }
       </xsl:for-each>
    </xsl:otherwise>
</xsl:choose>

std:colorList being the tree fragment of course. The above works fine, and is OK because the code is the same for the two alternatives but is not that large.
But for larger code fragments I wonder whether it is possible to avoid duplicating code by first declaring the variable based on the rtf, and then perform the code; something like

<xsl:variable name="colorList">
    <xsl:choose>
        <xsl:when test="function-available('msxsl:node-set')">
            <xsl:copy-of select="msxsl:node-set($std:colorList)"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:copy-of select="$std:colorList"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>

<xsl:for-each select="$colorList/color">
     tr.testid<xsl:value-of select="@testid"/> {
        color:<xsl:value-of select="."/>;
    }
</xsl:for-each>

But this does not work properly: MSXML complains about colorList not being a node-set, so it cannot be used in the xsl:for-each.

XSL transformation failed due to following error:
Expression must evaluate to a node-set.
-->$colorList<--/color

Note that in the working example, this error did not occur because of "copying" std:colorList into the colorList variable. Apparently it is an xsl parsing error, not a runtime one.
Should I use something else than xsl:copy-of? Or is there another way to achieve the same?

In case you wonder, std:colorList contents are as follows:

<std:colorList>
    <color testid="111">#FF0000</color>
    <color testid="999">#FFFF00</color>
</std:colorList>
Was it helpful?

Solution

Unfortunately in XSLT 1.0, when xsl:variable has contained instructions rather than a select attribute, the result is always an RTF. So your careful attempts to convert the RTF to a node-set come to nothing, because it's converted straight back again.

I'm afraid there's no clean workaround (other than moving to XSLT 2.0, of course). I would suggest structuring the code like this:

<xsl:choose>
    <xsl:when test="function-available('msxsl:node-set')">
        <xsl:apply-templates select="msxsl:node-set($std:colorList)/color" mode="z"/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:apply-templates select="$std:colorList/color" mode="z"/>
    </xsl:otherwise>
</xsl:choose>

<xsl:template match="color" mode="z">
     tr.testid<xsl:value-of select="@testid"/> {
                color:<xsl:value-of select="."/>;
            }
</xsl:template>

OTHER TIPS

Just for the record, I add the final solution below. It is slightly different from what Michael proposed, adding a copy of the RTF to a variable before applying the template.
This is because otherwise MSXML still errors during xsl parsing (apparently it checks the apply-templates select value and concludes it is not correct when it is an RTF instead of a node-set. And, as Michael said, xsl:variable select attribute does just that: converting an RTF to a node-set.

<xsl:choose>
        <xsl:when test="function-available('msxsl:node-set')">
        <xsl:apply-templates select="msxsl:node-set($std:colorList)/color" mode="addTRclassToCSS"/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:variable name="colorList" select="$std:colorList"/>
        <xsl:apply-templates select="$colorList" mode="addTRclassToCSS"/>
    </xsl:otherwise>
</xsl:choose>


<xsl:template match="color" mode="addTRclassToCSS">
                tr.testid<xsl:value-of select="@testid"/> {
                color:<xsl:value-of select="."/>;
                }
</xsl:template>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top