Issue with using exslt str:replace with a nodeset

ragamufin

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   "&#160;">
    <!ENTITY yen    "&#165;">
    <!ENTITY circle  "&#9679;">
    <!ENTITY raquo "&#187;">
]>
<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!

michael.hor257k

I can use the function normally to replace strings by passing in just strings for the 2nd and 3rd parameters.

It could be a problem with how the function is implemented in your processor. I suggest you eliminate all other possible causes of failure and try applying the following stylesheet:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:template match="/input">
    <output>
        <xsl:choose>
            <xsl:when test="function-available('str:replace')">
                <xsl:value-of select="str:replace(string, search, replace)" />  
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>function str:replace() is not supported</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
    </output>
</xsl:template>

</xsl:stylesheet>

to this input:

<input>
    <string>Mary had a little lamb, its fleece was white as snow. 
And everywhere that Mary went, the lamb was sure to go.</string>
    <search>Mary</search>
    <search>lamb</search>
    <search>fleece</search>
    <replace>John</replace>
    <replace>dog</replace>
    <replace>fur</replace>
</input>

and report back the results.


Continued:

Trying that I get <output>John had a little lamb, its fleece was white as snow. And everywhere that John went, the lamb was sure to go.</output>.

Well then obviously the function is not implemented according to the specification. That's not such a bad thing, since most processors do not implement the str:replace() function at all. All you need to is fill-in the missing part by calling a named template before calling the function, for example:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:exsl="http://exslt.org/common"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl str">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:variable name="dictionary"> 
    <search>Mary</search>
    <search>lamb</search>
    <search>fleece</search>
    <replace>John</replace>
    <replace>dog</replace>
    <replace>fur</replace>
</xsl:variable>

<xsl:template match="/input">
    <output>
        <xsl:call-template name="multi-replace">
            <xsl:with-param name="string" select="string"/>
            <xsl:with-param name="search-strings" select="exsl:node-set($dictionary)/search"/>
            <xsl:with-param name="replace-strings" select="exsl:node-set($dictionary)/replace"/>
        </xsl:call-template>
    </output>
</xsl:template>

<xsl:template name="multi-replace">
    <xsl:param name="string"/>
    <xsl:param name="search-strings"/>
    <xsl:param name="replace-strings"/>
    <xsl:choose>
        <xsl:when test="$search-strings">
            <xsl:call-template name="multi-replace">
                <xsl:with-param name="string" select="str:replace($string, $search-strings[1], $replace-strings[1])"/> 
                <xsl:with-param name="search-strings" select="$search-strings[position() > 1]"/>
                <xsl:with-param name="replace-strings" select="$replace-strings[position() > 1]"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$string"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Applied to the following test input:

<input>
    <string>Mary had a little lamb, its fleece was white as snow. And everywhere that Mary went, the lamb was sure to go.</string>
</input>

you should be seeing the following result:

<?xml version="1.0" encoding="utf-8"?>
<output>John had a little dog, its fur was white as snow. And everywhere that John went, the dog was sure to go.</output>

(I can't test this myself, since none of my processors support the function).

This should get you very close to the specified behavior, except for one thing: the specification states that "The longest search strings are replaced first". If you want to implement this too, you must sort the dictionary strings first - and if you want the implementation to be simple, you should enter the dictionary string as pairs.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

str_replace issue with the characters "<" and ">" in PHP 5.2

From Dev

Using a function in str_replace

From Dev

Replace a nodeset in XmlDocument with another XmlDocument file

From Dev

Replace a nodeset in XmlDocument with another XmlDocument file

From Dev

Issue using replace function in python

From Dev

Issue in using Preg_replace

From Dev

regex / str_replace: issue with mailto and smiley combination

From Dev

PHP str_replace array to remove spaces and reaplce with - and / with - also issue

From Dev

Python - Using str.replace with a wildcard

From Dev

Using double quotes on str_replace

From Dev

PHP str_replace tags with using arrays

From Dev

Using str_replace to alter the_title()

From Dev

Foreach loop using multiple str_replace

From Dev

failed when using str_replace

From Dev

How to replace multiple strings in PHP using str_replace?

From Dev

PHP replace multiple value using str_replace?

From Dev

How to replace multiple strings in PHP using str_replace?

From Dev

Replace two "words" in a string using str_replace

From Dev

Trying to replace a () in a string in R using str_replace

From Dev

How to improve eXSLT performance problems when using functions

From Dev

How to improve eXSLT performance problems when using functions

From Dev

xslt replace value based on a match from an imported nodeset

From Dev

Facing issue in replace string in a file using vbscript

From Dev

Issue Using SQL Replace Function with Variable

From Dev

Using preg_replace in foreach loop issue

From Dev

Using explode after using str_replace PHP

From Dev

How to replace part of str using `re.sub()`?

From Dev

How to get value after using str_replace once?

From Dev

Using str_replace to change text within PHP template

Related Related

  1. 1

    str_replace issue with the characters "<" and ">" in PHP 5.2

  2. 2

    Using a function in str_replace

  3. 3

    Replace a nodeset in XmlDocument with another XmlDocument file

  4. 4

    Replace a nodeset in XmlDocument with another XmlDocument file

  5. 5

    Issue using replace function in python

  6. 6

    Issue in using Preg_replace

  7. 7

    regex / str_replace: issue with mailto and smiley combination

  8. 8

    PHP str_replace array to remove spaces and reaplce with - and / with - also issue

  9. 9

    Python - Using str.replace with a wildcard

  10. 10

    Using double quotes on str_replace

  11. 11

    PHP str_replace tags with using arrays

  12. 12

    Using str_replace to alter the_title()

  13. 13

    Foreach loop using multiple str_replace

  14. 14

    failed when using str_replace

  15. 15

    How to replace multiple strings in PHP using str_replace?

  16. 16

    PHP replace multiple value using str_replace?

  17. 17

    How to replace multiple strings in PHP using str_replace?

  18. 18

    Replace two "words" in a string using str_replace

  19. 19

    Trying to replace a () in a string in R using str_replace

  20. 20

    How to improve eXSLT performance problems when using functions

  21. 21

    How to improve eXSLT performance problems when using functions

  22. 22

    xslt replace value based on a match from an imported nodeset

  23. 23

    Facing issue in replace string in a file using vbscript

  24. 24

    Issue Using SQL Replace Function with Variable

  25. 25

    Using preg_replace in foreach loop issue

  26. 26

    Using explode after using str_replace PHP

  27. 27

    How to replace part of str using `re.sub()`?

  28. 28

    How to get value after using str_replace once?

  29. 29

    Using str_replace to change text within PHP template

HotTag

Archive