Tamás
Tamás

Reputation: 398

Iterate over the elements of whitespace-separated list

I am trying to find out which is the simplest way to iterate over the elements of a whitespace-separated list using XSL. Let's say we have the following XML data file:

<?xml version="1.0" encoding="UTF-8"?>
<data>
    <!-- this list has 6 items -->
    <list>this is a list of strings</list>
</data>

The list element could be defined in XML Schema like this:

<xs:element name="list" type="strlist" />

<xs:simpleType name="strlist">
    <xs:list itemType="xs:string" />
</xs:simpleType>

I am not sure whether the XSL specification supports this construct directly, but I think it should since it is usable in XML Schema.

Any help would be highly appreciated.

Upvotes: 0

Views: 1762

Answers (2)

Michael Kay
Michael Kay

Reputation: 163342

XSLT 2.0 does support this directly: in a schema-aware tranformation, you can write

<xsl:for-each select="data(list)">
  ...
</xsl:for-each>

and if the element "list" is defined in the schema with a list type, this will iterate over the tokens.

But you can also do it without a schema by writing

<xsl:for-each select="tokenize(list, '\s+')">...</xsl:for-each>

In XSLT 1.0, you need to use recursive named templates; you can find an off-the-shelf str:tokenize template to copy into your stylesheets at www.exslt.org.

Upvotes: 1

G. Ken Holman
G. Ken Holman

Reputation: 4403

XML Schema predates XSLT 2.0 and so XSLT 2.0 can accommodate this with tokenize().

XSLT 1.0 predates XML Schema so you need a recursive template call that chops up the string:

T:\ftemp>type tokenize.xml
<?xml version="1.0" encoding="UTF-8"?>
<data>
    <!-- this list has 6 items -->
    <list>this is a list of strings</list>
</data>
T:\ftemp>xslt tokenize.xml tokenize.xsl
this,is,a,list,of,strings
T:\ftemp>type tokenize.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">

<xsl:output method="text"/>

<xsl:template match="data">
  <xsl:call-template name="tokenize">
    <xsl:with-param name="string" select="normalize-space(list)"/>
  </xsl:call-template>
</xsl:template>

<xsl:template name="tokenize">
  <xsl:param name="string"/>
  <xsl:choose>
    <xsl:when test="contains($string,' ')">
      <xsl:value-of select="substring-before($string,' ')"/>
      <xsl:text>,</xsl:text>
      <xsl:call-template name="tokenize">
        <xsl:with-param name="string" select="substring-after($string,' ')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$string"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>
T:\ftemp>xslt2 tokenize.xml tokenize2.xsl
this,is,a,list,of,strings
T:\ftemp>type tokenize2.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">

<xsl:output method="text"/>

<xsl:template match="data">
  <xsl:value-of select="tokenize(list,'\s+')" separator=","/>
</xsl:template>

</xsl:stylesheet>
T:\ftemp>

Upvotes: 1

Related Questions