Is it possible to make a for-each loop in XSLT not for a node set, but for my own collection of elements? For example, I split some string and have a string collection as a result. And I need to create a node for each item in the collection. I know that issue can be solved with a recursive template, but I want to know if it is possible to avoid a recursion.
There are two obvious, straightforward solutions, one of which is supported only in XSLT 2.0:
This works both with XSLT 1.0 and XSLT 2.0.
Define your own namespace and place your node-set as children of an element in this namespace, which element is globally placed in the stylesheet (child of the <xsl:stylesheet> instruction.)
Here is an example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="my"
>
<xsl:output method="text"/>
<my:nodes>
<string>Hello </string>
<string>World</string>
</my:nodes>
<xsl:variable name="vLookup"
select="document('')/*/my:nodes/*"/>
<xsl:param name="pSearchWord" select="'World'"/>
<xsl:template match="/">
<xsl:if test="$pSearchWord = $vLookup">
<xsl:value-of select=
"concat('Found the word ', $pSearchWord)"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the result is:
Found the word World
Do note that we do not need the xxx:node-set() extension function at all.
In XSLT 2.0 / XPath 2.0 one can always use the sequence type. For example, one can simply define a variable to contain a sequence of strings in this way:
<xsl:variable name="vLookup" as="xs:string*"
select="'Hello', 'World'"/>
and use it as in the following transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xsl:output method="text"/>
<xsl:variable name="vLookup" as="xs:string*"
select="'Hello', 'World'"/>
<xsl:param name="pSearchWord" select="'World'"/>
<xsl:template match="/">
<xsl:if test="$pSearchWord = $vLookup">
<xsl:value-of select=
"concat('Found the word ', $pSearchWord)"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
It is possible to do that using the XPath extension function node-set(). This function is supported e.g. by the msxsl and exslt extensions.
MSDN gives an example for how to use the msxsl:node-set() function with an xsl:for-each:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://www.contoso.com"
version="1.0">
<xsl:variable name="books">
<book author="Michael Howard">Writing Secure Code</book>
<book author="Michael Kay">XSLT Reference</book>
</xsl:variable>
<xsl:template match="/">
<authors>
<xsl:for-each select="msxsl:node-set($books)/book">
<author><xsl:value-of select="@author"/)</author>
</xsl:for-each>
</authors>
</xsl:template>
</xsl:stylesheet>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With