Change value in <xsl:value-of select="image"/> - xslt

I would like to change an image path.
The code to display the image is:
<xsl:value-of select="image"/>
the image path it takes from the feed is:
https://www.website.com/media/catalog/product/1/7/image.jpg
what i would like is:
https://www.website.com/media/catalog/product/thumbnail/folder/1/7/image.jpg
i tried the following:
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="with"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$with"/>
<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="with" select="$with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="image"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="root">
<xsl:for-each select="product[position() = 1]">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="'https://www.website.com/media/catalog/product/'"/>
<xsl:with-param name="replace" select="'product/'" />
<xsl:with-param name="with" select="'product/thumbnail/folder/'"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
the output is:
https://www.website.com/media/catalog/product/thumbnail/folder/https://www.website.com/media/catalog/product/image.jpg
not getting a real error message, it is just showing twice the http part

Couldn't this be a lot simpler? For example, consider:
XML
<root>
<product>
<image>https://www.website.com/media/catalog/product/1/7/image.jpg</image>
</product>
<product>
<image>https://www.website.com/media/catalog/product/2/56/image.jpg</image>
</product>
<product>
<image>https://www.website.com/media/catalog/product/3/123/image.jpg</image>
</product>
</root>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<images>
<xsl:for-each select="product">
<image>
<xsl:text>https://www.website.com/media/catalog/product/thumbnail/folder/</xsl:text>
<xsl:value-of select="substring-after(image, 'https://www.website.com/media/catalog/product/')" />
</image>
</xsl:for-each>
</images>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<images>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/1/7/image.jpg</image>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/2/56/image.jpg</image>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/3/123/image.jpg</image>
</images>

Related

How to Iterate through nodes and skip duplicate nodes with same value using a variable

i have a xml like,
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>xxx</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">ABC/DEF/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>yyy</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">LMN/OPQ/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>mmm</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">XYZ/GHY/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>nnn</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">AJK/UTL/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
My xslt,
<xsl:template name="substring-after-last">
<xsl:param name="string" />
<xsl:param name="delimiter" />
<xsl:choose>
<xsl:when test="contains($string, $delimiter)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string"
select="substring-after($string, $delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:for-each select="select="//DESIGN-FUNCTION-PROTOTYPE/ea:TYPE-TREF[#TYPE='DESIGN-FUNCTION-TYPE']">
<xsl:variable name="myVar" select="current()"/>
<xsl:variable name="taskName" select="../ea:SHORT-NAME"/>
<xsl:variable name="Var7">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="$myVar" />
<xsl:with-param name="delimiter" select="'/'" />
</xsl:call-template>
</xsl:variable>
<varoutput>
<xsl:value-of select="$Var7"/>
</varoutput>
</xsl:for-each>
My intention here is to iterate all the 'DESIGN-FUNCTION-PROTOTYPE' elements and display the sub-string of 'TYPE-TREF' value, but if a sub-string of 'TYPE-TREF' value has already been read..i must skip that element.
Expected output,
123
456
And Not,
123
123
456
456
In general I should consider only the first occurrence and skip the rest.
To do this in pure XSLT 1.0, without relying on processor-specific extensions, you could do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="k1" match="DESIGN-FUNCTION-PROTOTYPE" use="substring-after(substring-after(TYPE-TREF, '/'), '/')"/>
<xsl:template match="/Root">
<root>
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE[count(. | key('k1', substring-after(substring-after(TYPE-TREF, '/'), '/'))[1]) = 1]">
<varoutput>
<xsl:value-of select="substring-after(substring-after(TYPE-TREF, '/'), '/')" />
</varoutput>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/bFN1y9s
This is of course assuming that the value you're after is always the third "token" in TYPE-TREF. Otherwise you would have to do something similar to your attempt:
XSLT 1.0 + EXSLT node-set() function
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="k1" match="value" use="."/>
<xsl:template match="/Root">
<!-- EXTRACT VALUES -->
<xsl:variable name="values">
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE">
<value>
<xsl:call-template name="last-token">
<xsl:with-param name="text" select="TYPE-TREF"/>
</xsl:call-template>
</value>
</xsl:for-each>
</xsl:variable>
<!-- OUTPUT -->
<root>
<xsl:for-each select="exsl:node-set($values)/value[count(. | key('k1', .)[1]) = 1]">
<varoutput>
<xsl:value-of select="." />
</varoutput>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template name="last-token">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'/'"/>
<xsl:choose>
<xsl:when test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="last-token">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/bFN1y9s/1
Assuming you use Xalan you should have access to the EXSLT str:split function (http://xalan.apache.org/xalan-j/apidocs/org/apache/xalan/lib/ExsltStrings.html#split(java.lang.String,%20java.lang.String):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings" exclude-result-prefixes="str" version="1.0">
<xsl:key name="group" match="DESIGN-FUNCTION-PROTOTYPE/TYPE-TREF"
use="str:split(., '/')[last()]"/>
<xsl:template match="Root">
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE/TYPE-TREF[generate-id() = generate-id(key('group', str:split(., '/')[last()])[1])]">
<varoutput>
<xsl:value-of select="str:split(., '/')[last()]"/>
</varoutput>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Transforms
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>xxx</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">ABC/DEF/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>yyy</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">LMN/OPQ/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>mmm</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">XYZ/GHY/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>nnn</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">AJK/UTL/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
</Root>
into
<?xml version="1.0" encoding="UTF-8"?><varoutput>123</varoutput><varoutput>456</varoutput>
with Xalan Java and Xalan Java XSLTC.
Or, as suggested in a comment, if you simply want to find the distinct values you can use set:distinct e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:str="http://exslt.org/strings"
xmlns:set="http://exslt.org/sets"
exclude-result-prefixes="exsl str set"
version="1.0">
<xsl:template match="Root">
<xsl:variable name="split-values">
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE/TYPE-TREF">
<xsl:copy-of select="str:split(., '/')[last()]"/>
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="set:distinct(exsl:node-set($split-values)/node())"/>
</xsl:template>
</xsl:stylesheet>

How to call template for each dictionary elements?

This is my xml:
<A>
<D>dd</D>
<E>ee</E>
<B>
<C>1</C>
<C>2</C>
</B>
</A>
This was my xsl. I had to call template1 and template2 only few times for the same params:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/A">
<xsl:call-template name="items1">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="items2">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
...
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/D"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
<xsl:template name="template2">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/E"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
</xsl:stylesheet>
Now, I have to call my templates about 350 times for template1 and 350 times for template2, I have many key, value pairs.
I thought to make one dictionary with key value pairs and iterate over it on template1 and template2
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="values">
<item key="A" value="aa"/>
<item key="B" value="bb"/>
...
</xsl:variable>
<xsl:template match="/A">
<xsl:for-each select="$values">
<xsl:call-template name="items1">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
<xsl:for-each select="$values">
<xsl:call-template name="items2">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
...
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/D"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
<xsl:template name="template2">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/E"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
</xsl:stylesheet>
My question is that Is it good idea to solve my problem. How to get root node in templates, now current node is "item". I wanted to get value od D node at my template.
It's difficult to answer your question without seeing the complete picture. Without it I can only guess you want to do:
<xsl:for-each select="$prodDict/item">
<xsl:call-template name="Item">
<xsl:with-param name="param1" select="#key"/>
<xsl:with-param name="param" select="#value"/>
</xsl:call-template>
</xsl:for-each>
Note that this requires XSLT 2.0 or higher.
Added:
How to get root node in templates, now current node is "item".
A convenient method is to place the root node in a global variable, so that it's accessible from anywhere. Here's a simplified example:
XML
<A>
<D>dd</D>
<E>ee</E>
<B>
<C>1</C>
<C>2</C>
</B>
</A>
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="values">
<item key="A" value="aa"/>
<item key="B" value="bb"/>
</xsl:variable>
<xsl:variable name="xml" select="/" />
<xsl:template match="/">
<result>
<xsl:for-each select="$values/item">
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="#key"/>
<xsl:with-param name="param2" select="#value"/>
</xsl:call-template>
</xsl:for-each>
</result>
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="param1"/>
<xsl:param name="param2"/>
<item>
<d>
<xsl:value-of select="$xml/A/D"/>
</d>
<key>
<xsl:value-of select="$param1"/>
</key>
<value>
<xsl:value-of select="$param2"/>
</value>
</item>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<result>
<item>
<d>dd</d>
<key>A</key>
<value>aa</value>
</item>
<item>
<d>dd</d>
<key>B</key>
<value>bb</value>
</item>
</result>

Split a string with "|" Char to columns

I'm a bit new to this language so I have several doubts.
I'm working to process an xml to display some data on pdf form.
But there a few strings that have "|" so I can split the data to display properly.
Here is the example of the input data:
<root>
<reference>
<NroLinRef>12</NroLinRef>
<CodRef>I20</CodRef>
<RazonRef>Data1|Data2|Data3|Data4|Data5|Data6|Data7</RazonRef>
</reference>
</root>
In the output I need something like this so I can display in order in row with cells so data must be clear to read.
<root>
<Reference>
<NroLinRef>12</NroLinRef>
<CodRef>I20</CodRef>
<Data1>Data1</Data1>
<Data2>Data2</Data2>
<Data3>Data3</Data3>
<Data4>Data4</Data4>
<Data5>Data5</Data5>
<Data6>Data6</Data6>
<Data7>Data7</Data7>
</Reference>
</root>
To do this I have been using other code that is from another question but can't find how to get the name to be updated or customized.
And the output I get is actually like this:
<root>
<Reference>
<NroLinRef>12</NroLinRef>
<CodRef>I20</CodRef>
<Data>Data1</Data>
<Data>Data2</Data>
<Data>Data3</Data>
<Data>Data4</Data>
<Data>Data5</Data>
<Data>Data6</Data>
<Data>Data7</Data>
</Reference>
</root>
This is the XSL i'm using
<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="Referencia/RazonRef" name="tokenize">
<xsl:param name="text" select="."/>
<xsl:param name="separator" select="'|'"/>
<xsl:choose>
<xsl:when test="not(contains($text, $separator))">
<Data>
<xsl:value-of select="normalize-space($text)"/>
</Data>
</xsl:when>
<xsl:otherwise>
<Data>
<xsl:value-of select="normalize-space(substring-before($text, $separator))"/>
</Data>
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $separator)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
How can I get the output I want?
The expected result can be achieved by applying the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RazonRef" name="tokenize">
<xsl:param name="text" select="."/>
<xsl:param name="separator" select="'|'"/>
<xsl:param name="i" select="1"/>
<xsl:element name="Data{$i}">
<xsl:value-of select="substring-before(concat($text, $separator), $separator)"/>
</xsl:element>
<xsl:if test="contains($text, $separator)">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $separator)"/>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

XSLT Transform to XML

I have some xml in the following format:
<top>
<topValue Value="1#1#5" />
<topValue Value="2#2#10" />
<topValue Value="1#1#3" />
<topValue Value="2#2#30" />
</top>
and output should look like that:
<boo>
<booEnrty>
<v>5</v>
<v>10</v>
</booEnrty>
<booEnrty>
<v>3</v>
<v>30</v>
</booEnrty>
</boo>
my XSLT to transform
<boo>
<xsl:for-each select="top/topValue">
<xsl:if test="position() mod 2 = 0">
<booEnrty>
<v><xsl:value-of select="substring-after(substring-after(#Value,'#'),'#')"/></v>
</booEnrty>
</xsl:if>
</xsl:for-each>
</boo>
What should the XSLT document look like to do this transform?
Any ideas?
Thanks
Maybe someone got a better approach to this, but the XSLT below works for your case.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="top">
<boo>
<xsl:apply-templates select="topValue[position() mod 2 = 1]"/>
</boo>
</xsl:template>
<xsl:template match="topValue[position() mod 2 = 1]">
<booEntry>
<v>
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="#Value" />
<xsl:with-param name="delimiter" select="'#'" />
</xsl:call-template>
</v>
<xsl:apply-templates select="following-sibling::*[1]"/>
</booEntry>
</xsl:template>
<xsl:template match="topValue">
<v>
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="#Value" />
<xsl:with-param name="delimiter" select="'#'" />
</xsl:call-template>
</v>
</xsl:template>
<xsl:template name="substring-after-last">
<xsl:param name="string" />
<xsl:param name="delimiter" />
<xsl:choose>
<xsl:when test="contains($string, $delimiter)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string"
select="substring-after($string, $delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$string"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
How about something short and simple?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/top">
<boo>
<xsl:for-each select="topValue[position() mod 2 = 1]">
<booEnrty>
<xsl:for-each select=". | following-sibling::topValue[1]">
<v>
<xsl:value-of select="substring-after(substring-after(#Value,'#'),'#')"/>
</v>
</xsl:for-each>
</booEnrty>
</xsl:for-each>
</boo>
</xsl:template>
</xsl:stylesheet>

Replace multiple characters in a single element using XSLT

I am in need to find and replace a entities and characters to strings. For example,   should be replaced by ' ' empty space, $ should be replaced by |doll|, % should be replaced by |perc|. I can use XSLT 1.0.
XML document:
<?xml version="1.0"?>
<chapter>
<math>
<mtext>This is $400, 300%to500%.</mtext>
</math>
</chapter>
XSLT 1.0 transformation tried:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="mtext">
<mtext>
<xsl:choose>
<xsl:when test="contains(.,' ') or contains(.,'$') or contains(.,'%')">
<xsl:if test="contains(.,' ')"><xsl:call-template name="replace-string"><xsl:with-param name="text" select="."/><xsl:with-param name="from"> </xsl:with-param><xsl:with-param name="to" select="' '"/></xsl:call-template><xsl:variable name="mtext_text" select="."/></xsl:if>
<xsl:variable name="mtext_text" select="."/>
<xsl:if test="contains(.,'$')"><xsl:call-template name="replace-string"><xsl:with-param name="text" select="mtext_text"/><xsl:with-param name="from">$</xsl:with-param><xsl:with-param name="to" select="'|doll|'"/></xsl:call-template><xsl:variable name="mtext_text" select="."/></xsl:if>
<xsl:variable name="mtext_text" select="."/>
<xsl:if test="contains(.,'%')"><xsl:call-template name="replace-string"><xsl:with-param name="text" select="mtext_text"/><xsl:with-param name="from">%</xsl:with-param><xsl:with-param name="to" select="'|perc|'"/></xsl:call-template></xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</mtext>
</xsl:template>
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="from"/>
<xsl:param name="to"/>
<xsl:choose>
<xsl:when test="contains($text, $from)">
<xsl:variable name="before" select="substring-before($text, $from)"/>
<xsl:variable name="after" select="substring-after($text, $from)"/>
<xsl:variable name="prefix" select="concat($before, $to)"/>
<xsl:copy-of select="$before"/>
<xsl:value-of select="$to" disable-output-escaping="yes"/>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$after"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Required output:
<?xml version='1.0' ?>
<mtext>This is |doll|400, 300|perc|to500|perc|.</mtext>
This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" extension-element-prefixes="xsl">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:reps>
<rep>
<old> </old>
<new xml:space="preserve"> </new>
</rep>
<rep>
<old>$</old>
<new>|doll|</new>
</rep>
<rep>
<old>%</old>
<new>|perc|</new>
</rep>
</my:reps>
<xsl:variable name="vReps" select="document('')/*/my:reps/*"/>
<xsl:template match="mtext">
<xsl:copy>
<xsl:call-template name="multiReplace"/>
</xsl:copy>
</xsl:template>
<xsl:template name="multiReplace">
<xsl:param name="pText" select="."/>
<xsl:param name="pRep" select="$vReps[1]"/>
<xsl:choose>
<xsl:when test="not($pRep)"><xsl:value-of select="$pText"/></xsl:when>
<xsl:otherwise>
<xsl:variable name="vReplaced">
<xsl:call-template name="replace">
<xsl:with-param name="pText" select="$pText"/>
<xsl:with-param name="pOld" select="$pRep/old"/>
<xsl:with-param name="pNew" select="$pRep/new"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="multiReplace">
<xsl:with-param name="pText" select="$vReplaced"/>
<xsl:with-param name="pRep" select="$pRep/following-sibling::*[1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="pText"/>
<xsl:param name="pOld"/>
<xsl:param name="pNew"/>
<xsl:if test="$pText">
<xsl:value-of select="substring-before(concat($pText,$pOld), $pOld)"/>
<xsl:if test="contains($pText, $pOld)">
<xsl:value-of select="$pNew"/>
<xsl:call-template name="replace">
<xsl:with-param name="pText" select="substring-after($pText, $pOld)"/>
<xsl:with-param name="pOld" select="$pOld"/>
<xsl:with-param name="pNew" select="$pNew"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<chapter>
<math>
<mtext>This is $400, 300%to500%.</mtext>
</math>
</chapter>
produces the wanted, correct result:
<mtext>This is |doll|400, 300|perc|to500|perc|.</mtext>