Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT for-each loop for element collection

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.

like image 691
brain_pusher Avatar asked Mar 04 '26 01:03

brain_pusher


2 Answers

There are two obvious, straightforward solutions, one of which is supported only in XSLT 2.0:

I. A general solution

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.

II. An XSLT 2.0 / XPath 2.0 solution

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>
like image 200
Dimitre Novatchev Avatar answered Mar 05 '26 19:03

Dimitre Novatchev


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>
like image 24
Dirk Vollmar Avatar answered Mar 05 '26 17:03

Dirk Vollmar