1

exslt 文字列置換関数を使用する場合、名前空間と思われる問題があります。ドキュメントhereに従って、exslt string replace 関数の nodeset 形式を使用して、ターゲット文字列内の複数の文字列を置き換えたいと思います。ただし、ノードセットの最初の文字列のみを置き換え、他の文字列は置き換えないようです。

これが私のファイルです:

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

出力でわかるように。関数を使用すると、最初のノードの文字列のみが置き換えられます。2番目のものはそうではありません。ご覧のとおり、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そこに作成されたすべてのノードが、渡すノードと同じ null 名前空間に配置され、機能します。ただし、何を試しても関数バージョンを機能させることができません。作成したファイルと xml ノードセットからすべての名前空間を削除しましたが、それでも機能しません。率直に言って、この名前空間のすべては、私にとって少し混乱を招きます。たぶん、それはまったく問題ではありません。

どんな助けでも大歓迎です、ありがとう!

4

1 に答える 1

1

この関数を通常どおり使用して、2 番目と 3 番目のパラメーターに文字列だけを渡すことで、文字列を置き換えることができます。

関数がプロセッサにどのように実装されているかに問題がある可能性があります。他の考えられる失敗の原因をすべて排除し、次のスタイルシートを適用することをお勧めします。

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

(私のプロセッサはこの機能をサポートしていないため、これを自分でテストすることはできません)。

これにより、指定された動作に非常に近づくはずですが、1 つの点を除きます。仕様には、「最長の検索文字列が最初に置き換えられる」と記載されています。これも実装したい場合は、最初に辞書文字列をソートする必要があります。実装を単純にしたい場合は、辞書文字列をペアとして入力する必要があります。

于 2014-09-26T17:02:09.887 に答える