XSLT - Wrapping up of string after delimiter count 5 (e.g. 5 "|") - xslt

I am newbie in XSLT. I need to wrap up a long string after certain number of delimiter occurs.
Example of such a string is : -
Jason|Michael|John|James|Rick|Paul|JenYee|Ray|Eliza|Shilpa|Abhishek|Patrick|Brent|Kevin|Jim
I don't want to use template for this due to some constraints.
However if its not possible - i am ok with the template.
The output should be like this:
Line 1: Jason|Michael|John|James|Rick| Line 2: Paul|JenYee|Ray|Eliza|Shilpa| Line 3: Abhishek|Patrick|Brent|Kevin|Jim

Use this recursive templates:
<xsl:template name="beforeSeparators">
<xsl:param name="start"/>
<xsl:param name="rest"/>
<xsl:param name="separator"/>
<xsl:param name="count"/>
<xsl:choose>
<xsl:when test="$count <= 0">
<xsl:value-of select="$start"/>
</xsl:when>
<xsl:when test="contains($rest,$separator)">
<xsl:call-template name="beforeSeparators">
<xsl:with-param name="start" select="concat($start,substring-before($rest,$separator),$separator)"/>
<xsl:with-param name="rest" select="substring-after($rest,$separator)"/>
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($start,$rest)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="wrap">
<xsl:param name="str"/>
<xsl:param name="separator"/>
<xsl:param name="separatorsPerLine"/>
<xsl:variable name="line">
<xsl:call-template name="beforeSeparators">
<xsl:with-param name="start" select="''"/>
<xsl:with-param name="rest" select="$str"/>
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="count" select="$separatorsPerLine"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat($line,'
')"/>
<xsl:if test="string-length($line) < string-length($str)">
<xsl:call-template name="wrap">
<xsl:with-param name="str" select="substring($str,string-length($line))"/>
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="separatorsPerLine" select="$separatorsPerLine"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
called like this:
<xsl:call-template name="wrap">
<xsl:with-param name="str" select="'Jason|Michael|John|James|Rick|Paul|JenYee|Ray|Eliza|Shilpa|Abhishek|Patrick|Brent|Kevin|Jim'"/>
<xsl:with-param name="separator" select="'|'"/>
<xsl:with-param name="separatorsPerLine" select="5"/>
</xsl:call-template>
produces:
Jason|Michael|John|James|Rick|
Paul|JenYee|Ray|Eliza|Shilpa|
Abhishek|Patrick|Brent|Kevin|
Here is my complete test XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template name="beforeSeparators">
<xsl:param name="start"/>
<xsl:param name="rest"/>
<xsl:param name="separator"/>
<xsl:param name="count"/>
<xsl:choose>
<xsl:when test="$count <= 0">
<xsl:value-of select="$start"/>
</xsl:when>
<xsl:when test="contains($rest,$separator)">
<xsl:call-template name="beforeSeparators">
<xsl:with-param name="start" select="concat($start,substring-before($rest,$separator),$separator)"/>
<xsl:with-param name="rest" select="substring-after($rest,$separator)"/>
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($start,$rest)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="wrap">
<xsl:param name="str"/>
<xsl:param name="separator"/>
<xsl:param name="separatorsPerLine"/>
<xsl:variable name="line">
<xsl:call-template name="beforeSeparators">
<xsl:with-param name="start" select="''"/>
<xsl:with-param name="rest" select="$str"/>
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="count" select="$separatorsPerLine"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat($line,'
')"/>
<xsl:if test="string-length($line) < string-length($str)">
<xsl:call-template name="wrap">
<xsl:with-param name="str" select="substring($str,string-length($line)+1)"/>
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="separatorsPerLine" select="$separatorsPerLine"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="wrap">
<xsl:with-param name="str" select="'Jason|Michael|John|James|Rick|Paul|JenYee|Ray|Eliza|Shilpa|Abhishek|Patrick|Brent|Kevin|'"/>
<xsl:with-param name="separator" select="'|'"/>
<xsl:with-param name="separatorsPerLine" select="5"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
(it just translate a fixed string,, so it can be applied to any XML)

If you can use XSLT 2.0, you could use tokenize().
Example ($input is the string in your question):
<xsl:value-of select="tokenize($input,'\|')[5 >= position()]" separator="|"/>
This will produce: Jason|Michael|John|James|Rick

Related

use multiple in <xsl:param name= select> xsl

I have the following code:
<xsl:variable name="landlord"><xsl:value-of select="agreement/landlord1name"/></xsl:variable>
<xsl:variable name="tenant"><xsl:value-of select="agreement/tenantname"/></xsl:variable>
<xsl:variable name="property"><xsl:value-of select="agreement/propertyname"/></xsl:variable>
<xsl:variable name="startdate"><xsl:value-of select="agreement/startdate"/></xsl:variable>
<xsl:variable name="period"><xsl:value-of select="agreement/agreementterm"/></xsl:variable>
<xsl:variable name="lbody">
<xsl:call-template name="replace-text-multi">
<xsl:with-param name="text" select="tatemplate/letterbody"/>
<xsl:with-param name="replaces" select="'#tenant #period'"/>
<xsl:with-param name="bys" select="$tenant"/>
<xsl:with-param name="delim" select="' '"/>
</xsl:call-template>
</xsl:variable>
This is the code for template name=replace-text:
<xsl:template name='replace-text'>
<xsl:param name='text'/>
<xsl:param name='replace'/>
<xsl:param name='by'/>
<xsl:choose>
<xsl:when test='contains($text, $replace)'>
<xsl:value-of select='substring-before($text, $replace)'/>
<xsl:value-of select='$by' disable-output-escaping='yes'/>
<xsl:call-template name='replace-text'>
<xsl:with-param name='text' select='substring-after($text, $replace)'/>
<xsl:with-param name='replace' select='$replace'/>
<xsl:with-param name='by' select='$by'/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='$text'/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This is the code for template name=replace-text-multi:
<xsl:template name="replace-text-multi">
<xsl:param name="text"/>
<xsl:param name="replaces"/>
<xsl:param name="bys"/>
<xsl:param name="delim"/>
<xsl:choose>
<xsl:when test="contains($replaces, $delim)">
<xsl:variable name="textSoFar">
<xsl:call-template name="replace-text-multi">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replaces" select="substring-after($replaces, $delim)"/>
<xsl:with-param name="bys" select="substring-after($bys, $delim)"/>
<xsl:with-param name="delim" select="$delim"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="replace-text">
<xsl:with-param name="text" select="$textSoFar"/>
<xsl:with-param name="replace" select="substring-before($replaces, $delim)"/>
<xsl:with-param name="by" select="substring-before($bys, $delim)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="replace-text">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replace" select="$replaces"/>
<xsl:with-param name="by" select="$bys"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
here I want to replace #tenant as $tenant and #period as $period but xsl cannot read select="$tenant $period". Someone please help
I think the problem is in your <xsl:when test="contains($replaces, $delim)"> condition. You need to call the replace-text first, and then pass the output of that to the replace-text-multi template with the remaining parameters.
<xsl:when test="contains($replaces, $delim)">
<xsl:variable name="textSoFar">
<xsl:call-template name="replace-text">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replace" select="substring-before($replaces, $delim)"/>
<xsl:with-param name="by" select="substring-before($bys, $delim)"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="replace-text-multi">
<xsl:with-param name="text" select="$textSoFar"/>
<xsl:with-param name="replaces" select="substring-after($replaces, $delim)"/>
<xsl:with-param name="bys" select="substring-after($bys, $delim)"/>
<xsl:with-param name="delim" select="$delim"/>
</xsl:call-template>
</xsl:when>
It might also be worth making your delimiters a different string, other than space, just in case your replacement strings have spaces in. You also need to change how you set the bys (where $delim is a variable containing your chosen delimiter)
<xsl:with-param name="bys" select="concat($tenant, $delim, $period)"/>
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/root">
<xsl:variable name="tenant"><xsl:value-of select="agreement/tenantname"/></xsl:variable>
<xsl:variable name="period"><xsl:value-of select="agreement/agreementterm"/></xsl:variable>
<xsl:variable name="delim" select="'||'" />
<xsl:call-template name="replace-text-multi">
<xsl:with-param name="text" select="tatemplate/letterbody"/>
<xsl:with-param name="replaces" select="concat('#tenant', $delim, '#period')"/>
<xsl:with-param name="bys" select="concat($tenant, $delim, $period)"/>
<xsl:with-param name="delim" select="$delim"/>
</xsl:call-template>
</xsl:template>
<xsl:template name='replace-text'>
<xsl:param name='text'/>
<xsl:param name='replace'/>
<xsl:param name='by'/>
<xsl:choose>
<xsl:when test='contains($text, $replace)'>
<xsl:value-of select='substring-before($text, $replace)'/>
<xsl:value-of select='$by' disable-output-escaping='yes'/>
<xsl:call-template name='replace-text'>
<xsl:with-param name='text' select='substring-after($text, $replace)'/>
<xsl:with-param name='replace' select='$replace'/>
<xsl:with-param name='by' select='$by'/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='$text'/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replace-text-multi">
<xsl:param name="text"/>
<xsl:param name="replaces"/>
<xsl:param name="bys"/>
<xsl:param name="delim"/>
<xsl:choose>
<xsl:when test="contains($replaces, $delim)">
<xsl:variable name="textSoFar">
<xsl:call-template name="replace-text">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replace" select="substring-before($replaces, $delim)"/>
<xsl:with-param name="by" select="substring-before($bys, $delim)"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="replace-text-multi">
<xsl:with-param name="text" select="$textSoFar"/>
<xsl:with-param name="replaces" select="substring-after($replaces, $delim)"/>
<xsl:with-param name="bys" select="substring-after($bys, $delim)"/>
<xsl:with-param name="delim" select="$delim"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="replace-text">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replace" select="$replaces"/>
<xsl:with-param name="by" select="$bys"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to this XML
<root>
<agreement>
<tenantname>Tenant Name</tenantname>
<agreementterm>1st Dec to 2nd Dec</agreementterm>
</agreement>
<tatemplate>
<letterbody>
Dear #tenant
You term is #period
Kind regards
</letterbody>
</tatemplate>
</root>
The following is output
Dear Tenant Name
You term is 1st Dec to 2nd Dec
Kind regards

How to write a CSV-parser using XSLT 1.0?

I need to make a CSV-parser using XSLT 1.0. I have tried a recursive approach, but I can't seem to match the line endrings, so the input to the printLine-template is always empty.
<xsl:template match="/">
<xsl:call-template name="printLine">
<xsl:with-param name="line">
<xsl:value-of select="substring-before(//csvText, '\n')"/>
</xsl:with-param>
<xsl:with-param name="remaining">
<xsl:value-of select="substring-after(//csvText, '\n')"/>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="printLine">
<xsl:param name="line"/>
<xsl:param name="remaining"/>
<xsl:value-of select="$line"/><br />
<xsl:if test="remaining != ''">
<xsl:call-template name="printLine">
<xsl:with-param name="line">
<xsl:value-of select="substring-before($remaining, '\n')"/>
</xsl:with-param>
<xsl:with-param name="remaining">
<xsl:value-of select="substring-after($remaining, '\n')"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
I found a solution to this:
<xsl:variable name="ls"><xsl:text>
</xsl:text></xsl:variable>
<xsl:variable name="fs"><xsl:text> </xsl:text></xsl:variable>
<xsl:template match="/">
<xsl:call-template name="printLine">
<xsl:with-param name="line" select="substring-before(//csvText, $ls)"/>
<xsl:with-param name="remaining" select="substring-after(//csvText, $ls)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="printLine">
<xsl:param name="line"/>
<xsl:param name="remaining"/>
<xsl:call-template name="printFields">
<xsl:with-param name="line" select="$line"/>
</xsl:call-template>
<xsl:if test="$remaining != ''">
<xsl:call-template name="printLine">
<xsl:with-param name="line" select="substring-before($remaining, $ls)"/>
<xsl:with-param name="remaining" select="substring-after($remaining, $ls)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="printFields">
<xsl:param name="line"/>
<!-- render each line and access each field using the getFieldByIndex-template. Example: -->
<div>
<h3>
<xsl:call-template name="getFieldByIndex">
<xsl:with-param name="index" select="1"/>
<xsl:with-param name="line" select="$line"/>
</xsl:call-template>
</h3>
<p>
<xsl:call-template name="getFieldByIndex">
<xsl:with-param name="index" select="4"/>
<xsl:with-param name="line" select="$line"/>
</xsl:call-template>
</p>
</div>
</xsl:template>
<xsl:template name="getFieldByIndex">
<xsl:param name="index"/>
<xsl:param name="line"/>
<xsl:choose>
<xsl:when test="$index > 0">
<xsl:call-template name="getFieldByIndex">
<xsl:with-param name="index" select="$index -1"/>
<xsl:with-param name="line" select="substring-after($line, $fs)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($line,$fs)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The line- and field separators are stored in the ls and fs variables. This example traverses a tab-serparated file. The printFields-template has to be customized for each use.

How to unescape Escaped-XML content with the help of XSLT?

My question is How to un-escape xml that has already been escaped.
I tried the code provided by Tomalak in response to How to unescape XML characters with help of XSLT?, but I can't get that to do what I want.
I have SoapMsg Xml. The body contains a few elements one of which is a String. This string
contains Escaped XML. This is often done in RPC SoapMsg because they don't allow complex
types. To Get around this they embed Escaped-Xml inside a String Element, see sXmlParameters in the input below.
Example Input:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:pan="http://wsdl.somebody.com/Stuff/">
<soap:Header />
<soap:Body>
<pan:SomeCommand>
<first>eefbb52a0fee443cbda838caffbc2654</first>
<second>f26eb2f5dabc457ca045e64585f7b185</second>
<sXmlParameters><PARAMETERS><TIMEOUTDATETIME>2011-03-15
2:09:48.997</TIMEOUTDATETIME></PARAMETERS></sXmlParameters>
</pan:SomeCommand>
</soap:Body>
</soap:Envelope>
I also see this data escaped with <![CDATA[>]]>, I need to un-escape it also.
Converted Output:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:pan="http://wsdl.somebody.com/Stuff/">
<soap:Header />
<soap:Body>
<pan:SomeCommand>
<first>eefbb52a0fee443cbda838caffbc2654</first>
<second>f26eb2f5dabc457ca045e64585f7b185</second>
<sXmlParameters>
<PARAMETERS>
<TIMEOUTDATETIME>2011-03-15 2:09:48.997</TIMEOUTDATETIME>
</PARAMETERS>
</sXmlParameters>
</pan:SomeCommand>
</soap:Body>
</soap:Envelope>
This will already take care of half of your problem – not the CDATA part:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//sXmlParameters">
<xsl:copy>
<xsl:call-template name="unescape">
<xsl:with-param name="escaped" select="string(.)"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="unescape">
<xsl:param name="escaped"/>
<xsl:choose>
<xsl:when test="contains($escaped,'<')">
<xsl:variable name="beforeelem" select="substring-before($escaped,'<')"/>
<xsl:variable name="elemname1" select="substring-before(substring-after($escaped,'<'),' ')"/>
<xsl:variable name="elemname2" select="substring-before(substring-after($escaped,'<'),'>')"/>
<xsl:variable name="elemname3" select="substring-before(substring-after($escaped,'<'),'/>')"/>
<xsl:variable name="hasattributes" select="string-length($elemname1) > 0 and ((string-length($elemname2)=0 or string-length($elemname1) < string-length($elemname2)) and (string-length($elemname3)=0 or string-length($elemname1) < string-length($elemname3)))"/>
<xsl:variable name="elemclosed" select="string-length($elemname3) > 0 and (string-length($elemname2)=0 or string-length($elemname3) < string-length($elemname2))"/>
<xsl:variable name="elemname">
<xsl:choose>
<xsl:when test="$hasattributes">
<xsl:value-of select="$elemname1"/>
</xsl:when>
<xsl:when test="not($elemclosed)">
<xsl:value-of select="$elemname2"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$elemname3"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="elemclosetag" select="concat('</',$elemname,'>')"/>
<xsl:variable name="innercontent">
<xsl:if test="not($elemclosed)">
<xsl:call-template name="skipper-before">
<xsl:with-param name="source" select="substring-after(substring-after($escaped,'<'),'>')"/>
<xsl:with-param name="delimiter" select="$elemclosetag"/>
</xsl:call-template>
</xsl:if>
</xsl:variable>
<xsl:variable name="afterelem">
<xsl:choose>
<xsl:when test="not($elemclosed)">
<xsl:call-template name="skipper-after">
<xsl:with-param name="source" select="substring-after(substring-after($escaped,'<'),'>')"/>
<xsl:with-param name="delimiter" select="$elemclosetag"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-after(substring-after($escaped,'<'),'/>')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$elemname}">
<xsl:if test="$hasattributes">
<xsl:call-template name="unescapeattributes">
<xsl:with-param name="escapedattributes">
<xsl:choose>
<xsl:when test="not($elemclosed)">
<xsl:value-of select="normalize-space(substring-after($elemname2,' '))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-after($elemname3,' '))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
<xsl:call-template name="unescape">
<xsl:with-param name="escaped" select="$innercontent"/>
</xsl:call-template>
</xsl:element>
<xsl:call-template name="unescape">
<xsl:with-param name="escaped" select="$afterelem"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="unescapetext">
<xsl:with-param name="escapedtext" select="$escaped"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="unescapeattributes">
<xsl:param name="escapedattributes"/>
<xsl:variable name="attrname" select="substring-before($escapedattributes,'=')"/>
<xsl:variable name="attrquote" select="substring($escapedattributes,string-length($attrname)+2,1)"/>
<xsl:variable name="attrvalue" select="substring-before(substring-after($escapedattributes,$attrquote),$attrquote)"/>
<xsl:variable name="afterattr" select="substring-after(substring-after($escapedattributes,$attrquote),$attrquote)"/>
<xsl:attribute name="{$attrname}">
<xsl:call-template name="unescapetext">
<xsl:with-param name="escapedtext" select="$attrvalue"/>
</xsl:call-template>
</xsl:attribute>
<xsl:if test="contains($afterattr,'=')">
<xsl:call-template name="unescapeattributes">
<xsl:with-param name="escapedattributes" select="normalize-space($afterattr)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="unescapetext">
<xsl:param name="escapedtext"/>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$escapedtext"/>
<xsl:with-param name="replace">&gt;</xsl:with-param>
<xsl:with-param name="by">></xsl:with-param>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="replace">&lt;</xsl:with-param>
<xsl:with-param name="by"><</xsl:with-param>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="replace">&amp;</xsl:with-param>
<xsl:with-param name="by">&</xsl:with-param>
</xsl:call-template>
</xsl:template>
<!-- replaces substrings in strings -->
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="by"/>
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$by"/>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="by" select="$by"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- returns the substring after the last delimiter -->
<xsl:template name="skipper-after">
<xsl:param name="source"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($source,$delimiter)">
<xsl:call-template name="skipper-after">
<xsl:with-param name="source" select="substring-after($source,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$source"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- returns the substring before the last delimiter -->
<xsl:template name="skipper-before">
<xsl:param name="source"/>
<xsl:param name="delimiter"/>
<xsl:param name="result"/>
<xsl:choose>
<xsl:when test="contains($source,$delimiter)">
<xsl:call-template name="skipper-before">
<xsl:with-param name="source" select="substring-after($source,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
<xsl:with-param name="result">
<xsl:if test="result!=''">
<xsl:value-of select="concat($result,$delimiter)"/>
</xsl:if>
<xsl:value-of select="substring-before($source,$delimiter)"/>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I found that I can use saxon to do this in a much simpler way using the following:
<xsl:template match="SomeCommand">
<sXmlParameters>
<xsl:apply-templates select="saxon:parse(.)" />
</sXmlParameters>
</xsl:template>
there is also saxon:seriralize() that can be used to escape the xml
thanks to all for you input
Wrote a SAX parser for xml-escaped strings in pure xsl 1.0+EXSLT
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:pxml="https://github.com/ilyakharlamov/pure-xsl/parseStringAsXML"
version="1.0">
<xsl:import href="https://raw.githubusercontent.com/ilyakharlamov/pure-xsl/master/parseStringAsXML.xsl"/>
<xsl:template match="/">
<xsl:call-template name="pxml:parseStringAsXML">
<xsl:with-param name="string"><PARAMETERS><TIMEOUTDATETIME>2011-03-152:09:48.997</TIMEOUTDATETIME></PARAMETERS></xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Output:
<PARAMETERS>
<TIMEOUTDATETIME>2011-03-152:09:48.997</TIMEOUTDATETIME>
</PARAMETERS>

Recursive Loop XSLT

All,
I have the below XSLT
<xsl:template name="loop">
<xsl:param name="count" select="1"/>
<xsl:if test="$count > 0">
<xsl:text> </xsl:text>
<xsl:value-of select="$count"/>
<xsl:call-template name="loop">
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
The way to call it is:
<xsl:call-template name="loop
<xsl:with-param name="count" select="100"/>
</xsl:call-template>
At the moment it displays numbers from 100 to 0 and space between them.
(100 99 98 97.....)
How can I change it to do the opposite ? (1 2 3 4....)
Many Thanks,
M
Use:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="loop">
<xsl:with-param name="count" select="100"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="loop">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:text> </xsl:text>
<xsl:value-of select="$limit - $count"/>
<xsl:call-template name="loop">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when this transformation is performed on any XML document (not used), the wanted result: 1 to 100 is produced.
Do note: This solution is tail-recursive and with many XSLT processors will be optimized so that recursion is eliminated. This means you can use it with $count set to millions without stack overflow or slow execution.
A non-tail recursive solution, like the one of #0xA3 crashes with stack-overflow (with Saxon 6.5.4) even with count = 1000
Simply change the order inside the template:
<xsl:template name="loop">
<xsl:param name="count" select="1"/>
<xsl:if test="$count > 0">
<xsl:call-template name="loop">
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
<xsl:value-of select="$count"/>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
Try this one.
<xsl:template name="loop">
<xsl:param name="inc"/>
<xsl:param name="str" select="1"/>
<xsl:if test="$str <= $inc">
<xsl:text> </xsl:text>
<xsl:value-of select="$str"/>
<xsl:call-template name="loop">
<xsl:with-param name="inc" select="$inc"/>
<xsl:with-param name="str" select="$str + 1"></xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:call-template name="loop">
<xsl:with-param name="inc" select="10"/>
</xsl:call-template>

XSL How to copy identical + add copy with attributes changes

I need to copy a node and its sub-nodes:
first: one identical copy
followed by a modified copy with some attributes values changed
Here is the extract to change:
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="4"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
</Configuration>
In the second copy I need to change all "Debug" to "Release" and some attribute values also.
Thanks
Thanks Koynov
I found however a solution using template modes:
<xsl:template match="Configuration[#Name='Debug|Win32']">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
<xsl:copy>
<xsl:attribute name="WholeProgramOptimization">TRUE</xsl:attribute>
<xsl:apply-templates select="#*|node()" mode="Release"/>
</xsl:copy>
</xsl:template>
With this template for all attributes:
<xsl:template match="#*" mode="Release">
<xsl:attribute name="{local-name()}">
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="replace" select="'Debug'"/>
<xsl:with-param name="with" select="'Release'"/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
and of-course the "replace-string" template.
Thanks again.
Here is a xslt transformation for your needs:
<xsl:template match="Configuration">
<xsl:copy-of select="."/>
<xsl:call-template name="CopyWithReplace">
<xsl:with-param name="Node" select="."/>
<xsl:with-param name="PatternToReplace" select="string('Debug')"/>
<xsl:with-param name="ValueToReplaceWith" select="string('Release')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="CopyWithReplace">
<xsl:param name="Node"/>
<xsl:param name="PatternToReplace"/>
<xsl:param name="ValueToReplaceWith"/>
<xsl:variable name="ReplacedNodeName">
<xsl:call-template name="SearchAndReplace">
<xsl:with-param name="input" select="name($Node)"/>
<xsl:with-param name="search-string" select="$PatternToReplace"/>
<xsl:with-param name="replace-string" select="$ValueToReplaceWith"/> </xsl:call-template>
</xsl:variable>
<xsl:element name="{$ReplacedNodeName}">
<xsl:for-each select="#*">
<xsl:variable name="ReplacedAttributeName">
<xsl:call-template name="SearchAndReplace">
<xsl:with-param name="input" select="name()"/>
<xsl:with-param name="search-string" select="$PatternToReplace"/>
<xsl:with-param name="replace-string" select="$ValueToReplaceWith"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ReplacedAttributeValue">
<xsl:call-template name="SearchAndReplace">
<xsl:with-param name="input" select="."/>
<xsl:with-param name="search-string" select="$PatternToReplace"/>
<xsl:with-param name="replace-string" select="$ValueToReplaceWith"/>
</xsl:call-template>
</xsl:variable>
<xsl:attribute name="{$ReplacedAttributeName}">
<xsl:value-of select="$ReplacedAttributeValue"/>
</xsl:attribute>
</xsl:for-each>
<xsl:for-each select="child::*" >
<xsl:call-template name="CopyWithReplace">
<xsl:with-param name="Node" select="."/>
<xsl:with-param name="PatternToReplace" select="$PatternToReplace"/>
<xsl:with-param name="ValueToReplaceWith" select="$ValueToReplaceWith"/>
</xsl:call-template>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template name="SearchAndReplace">
<xsl:param name="input"/>
<xsl:param name="search-string"/>
<xsl:param name="replace-string"/>
<xsl:choose>
<!-- See if the input contains the search string -->
<xsl:when test="$search-string and contains($input,$search-string)">
<!-- If so, then concatenate the substring before the search string to the replacement string
and to the result of recursively applying this template to the remaining substring. -->
<xsl:value-of select="substring-before($input,$search-string)"/>
<xsl:value-of select="$replace-string"/>
<xsl:call-template name="SearchAndReplace">
<xsl:with-param name="input" select="substring-after($input,$search-string)"/>
<xsl:with-param name="search-string" select="$search-string"/>
<xsl:with-param name="replace-string" select="$replace-string"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- There are no more occurrences of the search string so just return the current input string -->
<xsl:value-of select="$input"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Good luck.