1

在使用 exslt 字符串替换功能时,我认为是一个命名空间的问题。我想按照此处的文档使用 exslt 字符串替换函数的节点集形式替换目标字符串中的几个字符串。然而,它似乎只替换了节点集的第一个字符串,而不是其他字符串。

这是我的文件:

<!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>

这是输出:

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

正如您在输出中看到的那样。在使用该函数时,只有第一个节点的字符串被替换。第二个没有。如您所见,我将模板代码从 exslt.org 复制到文件中,起初它没有工作,直到我将它添加xmlns=""到 str:replace 模板中,如下所示:

<xsl:template name="str:replace" xmlns="">

那时模板表单起作用了,这让我相信这是一个名称空间问题。我相信在函数中对节点进行排序并创建自己的replace节点时,如下所示:

<replace search="{.}">
    <xsl:copy-of select="exsl:node-set($replace-nodes-rtf)/node()[$pos]" />
</replace>

该节点可能最终位于不同的命名空间中,因此后续循环无法解决它们。将xmlns属性添加到str:replace将其中创建的任何节点放入与我传入的节点相同的空命名空间中,然后它就可以工作了。但是,无论我尝试什么,我都无法使功能版本正常工作。我什至从文件和我创建的 xml 节点集中删除了所有命名空间,但它仍然不起作用。坦率地说,所有这些命名空间的东西让我有点困惑。也许这根本不是问题。

任何帮助将不胜感激,谢谢!

4

1 回答 1

1

我可以通过只为第二个和第三个参数传入字符串来正常使用该函数来替换字符串。

该功能在处理器中的实现方式可能存在问题。我建议您消除所有其他可能的失败原因并尝试应用以下样式表

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

对此输入

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

并报告结果。


继续:

尝试我得到<output>John had a little lamb, its fleece was white as snow. And everywhere that John went, the lamb was sure to go.</output>.

那么显然该功能没有按照规范实现。这并不是一件坏事,因为大多数处理器根本没有实现 str:replace() 函数。您只需在调用函数之前通过调用命名模板来填充缺失的部分,例如:

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

应用于以下测试输入

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

您应该会看到以下结果:

<?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>

(我自己无法对此进行测试,因为我的处理器都不支持该功能)。

这应该使您非常接近指定的行为,除了一件事:规范规定“首先替换最长的搜索字符串”。如果您也想实现这一点,则必须首先对字典字符串进行排序 - 如果您希望实现简单,您应该将字典字符串输入为 pairs

于 2014-09-26T17:02:09.887 回答