I've got an issue that I think is a namespace when using the exslt string replace function. I'd like to replace several strings in a target string by using the nodeset form of the exslt string replace function as per the documentation here. However it seems to only replace the first string of the nodeset and not the others.
Here is my file:
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp " ">
<!ENTITY yen "¥">
<!ENTITY circle "●">
<!ENTITY raquo "»">
]>
<xsl:stylesheet
version="1.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:str="http://exslt.org/strings"
xmlns:exsl="http://exslt.org/common"
xmlns:regexp="http://exslt.org/regular-expressions"
extension-element-prefixes="msxsl str exsl regexp">
<xsl:output
method="html"
indent="yes"
encoding="utf-8"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
/>
<!-- Start of the main template -->
<xsl:template match="/Top">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
<body>
<xsl:variable name="text">
-[this]- This is a test string -[that]-
-[this]- This is another test string -[that]-
</xsl:variable>
text: <xsl:value-of select="$text" disable-output-escaping="yes" />
<xsl:variable name="searches" xmlns="">
<replacements xmlns="">
<searches>
<search>-[this]-</search>
<search>-[that]-</search>
</searches>
<replaces>
<replace>**[this]**</replace>
<replace>**[that]**</replace>
</replaces>
</replacements>
</xsl:variable>
<xsl:variable name="search_set" select="exsl:node-set($searches)/replacements/searches/search" />
<xsl:variable name="replace_set" select="exsl:node-set($searches)/replacements/replaces/replace" />
search_set: <xsl:copy-of select="$search_set" />
replace_set: <xsl:copy-of select="$replace_set" />
<xsl:if test="$search_set">
replaced via function:
<xsl:value-of select="str:replace($text, $search_set, $replace_set)" disable-output-escaping="yes" />
replaced via template:
<xsl:variable name="replaced_tpl">
<xsl:call-template name="str:replace">
<xsl:with-param name="string" select="$text"/>
<xsl:with-param name="search" select="$search_set"/>
<xsl:with-param name="replace" select="$replace_set"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$replaced_tpl" disable-output-escaping="yes" />
</xsl:if>
</body>
</html>
</xsl:template><!-- / end main template -->
<xsl:template name="str:replace" xmlns="">
<xsl:param name="string" select="''" />
<xsl:param name="search" select="/.." />
<xsl:param name="replace" select="/.." />
<xsl:choose>
<xsl:when test="not($string)" />
<xsl:when test="not($search)">
<xsl:value-of select="$string" />
</xsl:when>
<xsl:when test="function-available('exsl:node-set')">
<!-- this converts the search and replace arguments to node sets
if they are one of the other XPath types -->
<xsl:variable name="search-nodes-rtf">
<xsl:copy-of select="$search" />
</xsl:variable>
<xsl:variable name="replace-nodes-rtf">
<xsl:copy-of select="$replace" />
</xsl:variable>
<xsl:variable name="replacements-rtf">
<xsl:for-each select="exsl:node-set($search-nodes-rtf)/node()">
<xsl:variable name="pos"
select="position()" />
<replace search="{.}">
<xsl:copy-of select="exsl:node-set($replace-nodes-rtf)/node()[$pos]" />
</replace>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="sorted-replacements-rtf">
<xsl:for-each select="exsl:node-set($replacements-rtf)/replace">
<xsl:sort select="string-length(@search)"
data-type="number"
order="descending" />
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
<xsl:call-template name="str:_replace">
<xsl:with-param name="string"
select="$string" />
<xsl:with-param name="replacements"
select="exsl:node-set($sorted-replacements-rtf)/replace" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">
ERROR: template implementation of str:replace relies on exsl:node-set().
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="str:_replace">
<xsl:param name="string"
select="''" />
<xsl:param name="replacements"
select="/.." />
<xsl:choose>
<xsl:when test="not($string)" />
<xsl:when test="not($replacements)">
<xsl:value-of select="$string" />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="replacement"
select="$replacements[1]" />
<xsl:variable name="search"
select="$replacement/@search" />
<xsl:choose>
<xsl:when test="not(string($search))">
<xsl:value-of select="substring($string, 1, 1)" />
<xsl:copy-of select="$replacement/node()" />
<xsl:call-template name="str:_replace">
<xsl:with-param name="string"
select="substring($string, 2)" />
<xsl:with-param name="replacements"
select="$replacements" />
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($string, $search)">
<xsl:call-template name="str:_replace">
<xsl:with-param name="string"
select="substring-before($string, $search)" />
<xsl:with-param name="replacements"
select="$replacements[position() > 1]" />
</xsl:call-template>
<xsl:copy-of select="$replacement/node()" />
<xsl:call-template name="str:_replace">
<xsl:with-param name="string"
select="substring-after($string, $search)" />
<xsl:with-param name="replacements"
select="$replacements" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="str:_replace">
<xsl:with-param name="string"
select="$string" />
<xsl:with-param name="replacements"
select="$replacements[position() > 1]" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
And here is the output:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml"><head><style type="text/css"></style></head><body>
text:
-[this]- This is a test string -[that]-
-[this]- This is another test string -[that]-
search_set: <search xmlns="">-[this]-</search><search xmlns="">-[that]-</search>
replace_set: <replace xmlns="">**[this]**</replace><replace xmlns="">**[that]**</replace>
replaced via function:
**[this]** This is a test string -[that]-
**[this]** This is another test string -[that]-
replaced via template:
**[this]** This is a test string **[that]**
**[this]** This is another test string **[that]**
</body></html>
As you can see in the output. In when using the function, only the string of the first node gets replaced. The second one does not. I copied the template code from exslt.org into the file as you can see and at first it didn't work until I added the xmlns=""
to the str:replace template like so:
<xsl:template name="str:replace" xmlns="">
At that point the template form works which leads me to believe this is a name space issue. I believe that in the function when it sorts the nodes and creates its own replace
nodes like so:
<replace search="{.}">
<xsl:copy-of select="exsl:node-set($replace-nodes-rtf)/node()[$pos]" />
</replace>
That node ends up in a different namespace maybe and so the subsequent loop cannot address them. Adding the xmlns
attribute to the str:replace
puts any nodes created therein in the same null namespace as the nodes I'm passing in and then it works. However no matter what I try I cannot get the function version to work. I even removed all namespaces from the file and the xml nodeset I create and still it doesn't work. Frankly all this namespace stuff is a bit confusing to me. Maybe that's not even the problem at all.
Any help would be greatly appreciated, thanks!