0

I have an XML with a text node, and I need to split this string into multiple chunks using XSLT 2.0. For example:

<tag>
    <text>This is a long string 1This is a long string 2This is a long string 3This is a long string 4</text>
</tag>

The output should be:

<tag>
    <text>This is a long string 1</text>
    <text>This is a long string 2</text>
    <text>This is a long string 3</text>
    <text>This is a long string 4</text>
</tag>

Note that I deliberately set the chunk size to the length of each statement so that the example is easier to read and write, but the transformation should accept any value (it is okay for this value to be hardcoded).

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
Paul
  • 105
  • 1
  • 12

1 Answers1

1

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pChunkSize" select="23"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="text/text()" name="chunk">
  <xsl:param name="pText" select="."/>

  <xsl:if test="string-length($pText) >0">
   <text><xsl:value-of select=
   "substring($pText, 1, $pChunkSize)"/>
   </text>
   <xsl:call-template name="chunk">
    <xsl:with-param name="pText"
    select="substring($pText, $pChunkSize+1)"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<tag>
    <text>This is a long string 1This is a long string 2This is a long string 3This is a long string 4</text>
</tag>

produces the wanted, correct result:

<tag>
    <text>
        <text>This is a long string 1</text>
        <text>This is a long string 2</text>
        <text>This is a long string 3</text>
        <text>This is a long string 4</text>
        <text/>
    </text>
</tag>

II. XSLT 2.0 solution (non-recursive):

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pChunkSize" select="23"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="text/text()">
  <xsl:variable name="vtheText" select="."/>
  <xsl:for-each select=
      "0 to string-length() idiv $pChunkSize">
   <text>
    <xsl:sequence select=
     "substring($vtheText, . *$pChunkSize +1, $pChunkSize) "/>
   </text>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Great job! But do you think it can be done without using recursion? Thanks. – Paul Oct 14 '11 at 17:09
  • @Paul: Not in XSLT 1.0 -- at least for unlimited number of chunks. If the number of chunks is known not to exceed some reasonably small limit, then this can be done without recursion. In XSLT 2.0 this can be done without recursion -- easily. I am curious to know what problem do you find with recursion? – Dimitre Novatchev Oct 14 '11 at 17:57
  • No problem at all. I was just wondering if I could avoid recursion. By the way, I *am* using XSLT 2.0. – Paul Oct 14 '11 at 19:25
  • 1
    @Paul: I have updated my answer and now it has an XSLT 2.0, non-recursive solution, as requested. In the future, please, tag your questions with xslt-2.0. :) – Dimitre Novatchev Oct 14 '11 at 19:52