Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xsl: how to split strings?

I want to split an address on semicolons (;) into rows separated by <br />:

e.g. if address=123 Elm Street, I want to output 123 Elm Street,

but if address=123 Elm Street;PO Box 222, I want to output

123 Elm Street<br />PO Box 222 

and if address=123 Elm Street;PO Box 222;c/o James Jones, I want to output

123 Elm Street<br />PO Box 222<br />c/o James Jones 

Is there a way to do this? (probably easy but I'm not that familiar with XSLT)

The plain XSL selector is

<xsl:value-of select="address"/> 

and I would like to modify this XSLT fragment to split on semicolon.


update: Apparently the answer involves the use of <xsl:call-template> and the functions substring-before() and substring-after().

But I'm a beginner to XSLT and I could really use some help for how to do this.

like image 323
Jason S Avatar asked Jan 30 '11 21:01

Jason S


People also ask

How do I substring in XSLT?

Substring is primarily used to return a section of a string or truncating a string with the help of the length provided. XSLT is incorporated with XPath while manipulating strings of text when XSLT access. The Substring function takes a string as an argument with the numbers one or two and an optional length.

What is substring after in XSLT?

substring-after() Function — Returns the substring of the first argument after the first occurrence of the second argument in the first argument. If the second argument does not occur in the first argument, the substring-after() function returns an empty string.

What is Tokenize in XSLT?

XSLT/XPath Reference: XSLT elements, EXSLT functions, XPath functions, XPath axes. str:tokenize() splits a string using a set of characters as delimiters that determine where the splits should occur, returning a node-set containing the resulting strings.

What is XSL sequence?

Used to construct arbitrary sequences. It may select any sequence of nodes and/or atomic values, and essentially adds these to the result sequence.


2 Answers

I. Plain XSLT 1.0 solution:

This transformation:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:output omit-xml-declaration="yes" indent="yes"/>   <xsl:template match="text()" name="split">   <xsl:param name="pText" select="."/>   <xsl:if test="string-length($pText)">    <xsl:if test="not($pText=.)">     <br />    </xsl:if>    <xsl:value-of select=     "substring-before(concat($pText,';'),';')"/>    <xsl:call-template name="split">     <xsl:with-param name="pText" select=      "substring-after($pText, ';')"/>    </xsl:call-template>   </xsl:if>  </xsl:template> </xsl:stylesheet> 

when applied on this XML document:

<t>123 Elm Street;PO Box 222;c/o James Jones</t> 

produces the wanted, corrected result:

123 Elm Street<br />PO Box 222<br />c/o James Jones 

II. FXSL 1 (for XSLT 1.0):

Here we just use the FXSL template str-map (and do not have to write recursive template for the 999th time):

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://fxsl.sf.net/" xmlns:testmap="testmap" exclude-result-prefixes="xsl f testmap" >    <xsl:import href="str-dvc-map.xsl"/>     <testmap:testmap/>     <xsl:output omit-xml-declaration="yes" indent="yes"/>     <xsl:template match="/">      <xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/>      <xsl:call-template name="str-map">        <xsl:with-param name="pFun" select="$vTestMap"/>        <xsl:with-param name="pStr" select=        "'123 Elm Street;PO Box 222;c/o James Jones'"/>      </xsl:call-template>    </xsl:template>      <xsl:template name="replace" mode="f:FXSL"          match="*[namespace-uri() = 'testmap']">       <xsl:param name="arg1"/>        <xsl:choose>        <xsl:when test="not($arg1=';')">         <xsl:value-of select="$arg1"/>        </xsl:when>        <xsl:otherwise><br /></xsl:otherwise>       </xsl:choose>     </xsl:template> </xsl:stylesheet> 

when this transformation is applied on any XML document (not used), the same, wanted correct result is produced:

123 Elm Street<br/>PO Box 222<br/>c/o James Jones 

III. Using XSLT 2.0

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:output omit-xml-declaration="yes" indent="yes"/>   <xsl:template match="text()">   <xsl:for-each select="tokenize(.,';')">    <xsl:sequence select="."/>    <xsl:if test="not(position() eq last())"><br /></xsl:if>   </xsl:for-each>  </xsl:template> </xsl:stylesheet> 

when this transformation is applied on this XML document:

<t>123 Elm Street;PO Box 222;c/o James Jones</t> 

the wanted, correct result is produced:

123 Elm Street<br />PO Box 222<br />c/o James Jones 
like image 55
Dimitre Novatchev Avatar answered Oct 23 '22 06:10

Dimitre Novatchev


If your XSLT processor supports EXSLT, you can use str:tokenize, otherwise, the link contains an implementation using functions like substring-before.

like image 42
Mormegil Avatar answered Oct 23 '22 06:10

Mormegil