Question

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 :-)

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top