klodye
klodye

Reputation: 103

XSLT take line by line from the text of some element

I'd like to ask if there is any way in XSLT to take line by line of text in some element and apply something to that line. For example i have

<screen>
Volume in drive C is SYSTEM         Serial number is 2350:717C    
Directory of  C:\

10/17/97   9:04         &lt;DIR&gt;    bin
10/16/97  14:11         &lt;DIR&gt;    DOS
10/16/97  14:40         &lt;DIR&gt;    Program Files
10/16/97  14:46         &lt;DIR&gt;    TEMP
10/17/97   9:04         &lt;DIR&gt;    tmp
10/16/97  14:37         &lt;DIR&gt;    WINNT
10/16/97  14:25             119  AUTOEXEC.BAT
2/13/94   6:21          54,619  COMMAND.COM
10/16/97  14:25             115  CONFIG.SYS
11/16/97  17:17      61,865,984  pagefile.sys
2/13/94   6:21           9,349  WINA20.386
</screen>

and I would like to take line by line and put white space (bracket, hyphen etc.) before every line.

Thank you for any help :-)

Upvotes: 2

Views: 5005

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

I. XSLT 2.0 solution:

<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="/*">
  <screen>
   <xsl:for-each select="tokenize(., '\r?\n')">
    line: <xsl:sequence select="."/>
   </xsl:for-each>
  </screen>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<screen>
    Volume in drive C is SYSTEM         Serial number is 2350:717C
    Directory of  C:\

    10/17/97   9:04         &lt;DIR&gt;    bin
    10/16/97  14:11         &lt;DIR&gt;    DOS
    10/16/97  14:40         &lt;DIR&gt;    Program Files
    10/16/97  14:46         &lt;DIR&gt;    TEMP
    10/17/97   9:04         &lt;DIR&gt;    tmp
    10/16/97  14:37         &lt;DIR&gt;    WINNT
    10/16/97  14:25             119  AUTOEXEC.BAT
    2/13/94   6:21          54,619  COMMAND.COM
    10/16/97  14:25             115  CONFIG.SYS
    11/16/97  17:17      61,865,984  pagefile.sys
    2/13/94   6:21           9,349  WINA20.386
</screen>

the wanted, correct result (each line of the text node is prepended by the string "line: ") is produced:

<screen>
    line: 
    line:     Volume in drive C is SYSTEM         Serial number is 2350:717C
    line:     Directory of  C:\
    line: 
    line:     10/17/97   9:04         &lt;DIR&gt;    bin
    line:     10/16/97  14:11         &lt;DIR&gt;    DOS
    line:     10/16/97  14:40         &lt;DIR&gt;    Program Files
    line:     10/16/97  14:46         &lt;DIR&gt;    TEMP
    line:     10/17/97   9:04         &lt;DIR&gt;    tmp
    line:     10/16/97  14:37         &lt;DIR&gt;    WINNT
    line:     10/16/97  14:25             119  AUTOEXEC.BAT
    line:     2/13/94   6:21          54,619  COMMAND.COM
    line:     10/16/97  14:25             115  CONFIG.SYS
    line:     11/16/97  17:17      61,865,984  pagefile.sys
    line:     2/13/94   6:21           9,349  WINA20.386
    line: </screen>

Explanation:

Appropriate use of the tokenize() function with second argument a RegEx that allows an optional CR to precede the NL character.


II. XSLT 1.0 solution:

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

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

  <xsl:if test="string-length($pText)">
   line: <xsl:text/>

   <xsl:value-of select=
   "substring-before(concat($pText, '&#xA;'), '&#xA;')"/>

   <xsl:call-template name="lines">
    <xsl:with-param name="pText" select=
     "substring-after($pText, '&#xA;')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

When this XSLT 1.0 transformation is applied on the same XML document (above), the wanted result (each line of the text node, prepended with the string "line: ") is produced:

   line: 
   line:     Volume in drive C is SYSTEM         Serial number is 2350:717C
   line:     Directory of  C:\
   line: 
   line:     10/17/97   9:04         &lt;DIR&gt;    bin
   line:     10/16/97  14:11         &lt;DIR&gt;    DOS
   line:     10/16/97  14:40         &lt;DIR&gt;    Program Files
   line:     10/16/97  14:46         &lt;DIR&gt;    TEMP
   line:     10/17/97   9:04         &lt;DIR&gt;    tmp
   line:     10/16/97  14:37         &lt;DIR&gt;    WINNT
   line:     10/16/97  14:25             119  AUTOEXEC.BAT
   line:     2/13/94   6:21          54,619  COMMAND.COM
   line:     10/16/97  14:25             115  CONFIG.SYS
   line:     11/16/97  17:17      61,865,984  pagefile.sys
   line:     2/13/94   6:21           9,349  WINA20.386

Explanation:

  1. Recursive named template to extract and output each next line. Stop condition -- when the string has zero length.

  2. Appropriate use of substring-before(), substring-after() and a sentinel technique to minimize code length and complexity.

Upvotes: 4

Martin Honnen
Martin Honnen

Reputation: 167571

Well with XSLT 2.0 as supported by Saxon 9 or AltovaXML tools and others you can use the tokenize function e.g.

<xsl:template match="screen">
  <xsl:for-each select="tokenize(., '\n')">
    <xsl:value-of select="concat('-', .)"/>
  </xsl:for-each>
</xsl:template>

With XSLT 1.0 you could check whether your processor supports an extension function like http://www.exslt.org/str/functions/tokenize/index.html.

Upvotes: 4

Related Questions