3

编辑: [从字符替换开始,我最终在Dimitre NovatchevRoland Bouman的帮助下发现了字符串替换

我认为示例代码足以解释要求..

这是示例 XML:

<root>
  <node1>text node</node1>
  <node2>space between the text</node2>
  <node3> has to be replaced with $</node3>
</root>

这是我期待的输出:

<root>
  <node1>text$node</node1>
  <node2>space$between$the$text</node2>
  <node3>$has$to$be$replaced$with$$</node3>
</root>

我尝试编写一个未显示所需输出的 ​​XSLT 代码。
这是代码:

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
  <xsl:template match="text()[.!='']">
    <xsl:call-template name="rep_space">
      <xsl:with-param name="text" select="."/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="rep_space">
    <xsl:param name="text"/>
    <xsl:variable name="temp" select="'&#x36;'"/> 
    <xsl:choose>
      <xsl:when test="contains(text,'&#x32;')">
        <xsl:call-template name="rep_space">
          <xsl:with-param name="text" select="concat((concat(substring-before(text,' '),temp)),substring-after(text,' '))"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

translate(., ' ', '$') 函数可以工作..但没有达到令人满意的程度..我的问题是..如果它是字符串而不是字符怎么办?我的意思是,假设我打算用“%20”替换''?还有一种情况,如果输入的 XML 不是“Pretty Print XML”,那么 XML 中出现的所有空格都被替换为 '$' ..

漂亮的打印 XML是具有适当缩进的文件,(通常我的输入 XML 从来没有这个)例如:

还有一个节点,这是@lower level

您可以观察到,节点之前没有“空格字符”<new> <test> ,但它们实际上是正确缩进的(使用 altova XMLSPY,我们可以在编辑菜单中给出一个简单的命令.. 将任何 XML 文件制作为“漂亮打印 XML”)..

如下例所示..

<new>
  <test>one more node</test>
   <test2>
    <child>this is @ lower level</child>
   </test2>
</new>

所有开始标签之前都有空格字符..<child>标签之前的空格比<test2>节点..

使用第二个示例 xml .. 所有空格字符都替换为 " %20".. 因此输出将是 ..

<new>
%20%20<test>one%20more%20node</test>
%20%20<test2>
%20%20%20%20<child>this%20is%20@%20lower%20level</child>
%20%20</test2>
</new>

当然这不是预期的..

Dimitre NovatchevRoland Bouman发布的解决方案还可以通过修改传递给被调用模板的参数来用另一个字符串替换一个字符串。

学习@Dimitre,@Roland 真是太棒了,我真的很感谢你们……

问候,
婴儿亲。

4

3 回答 3

8

根据Roland的意愿,这里有一个尾递归的解决方案

 <xsl:template name="replace">
  <xsl:param name="ptext"/>
  <xsl:param name="ppattern"/>
  <xsl:param name="preplacement"/>

  <xsl:choose>
     <xsl:when test="not(contains($ptext, $ppattern))">
      <xsl:value-of select="$ptext"/>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="substring-before($ptext, $ppattern)"/>
       <xsl:value-of select="$preplacement"/>
       <xsl:call-template name="replace">
         <xsl:with-param name="ptext"
           select="substring-after($ptext, $ppattern)"/>
         <xsl:with-param name="ppattern" select="$ppattern"/>
         <xsl:with-param name="preplacement" select="$preplacement"/>
       </xsl:call-template>
     </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

请注意,递归调用是模板中的最后一条指令——这就是使其成为尾递归的原因。尾递归的特性允许智能 XSLT 处理器(例如 Saxon 或 .NET XslCompiledTransform)优化代码,用简单的迭代代替递归。

即使调用的“嵌套”数百万,此类代码也不会以堆栈溢出异常告终,而非尾递归(和递归)代码通常会在大约 1000 个嵌套调用的深度引发此堆栈溢出(此实际上取决于可用内存的数量)。

如果 XSLT 处理器不够“智能”怎么办?是否有另一种技术可以避免深度递归调用堆栈溢出,它适用于每个XSLT 处理器?

问我一个单独的问题,我可能会告诉你:)

于 2010-02-25T15:01:46.213 回答
4

查看 XPath 翻译功能: http ://www.w3.org/TR/xpath/#function-translate

<xsl:template match="text()">
    <xsl:value-of select="translate(., ' ', '$')"/>
</xsl:template>

如果它不是单个字符,而是您必须替换的字符串,则需要付出更多的努力,并且您需要一个模板来递归替换字符串:

<xsl:template match="text()[not(../*)]">
    <xsl:call-template name="replace">
        <xsl:with-param name="text" select="."/>
        <xsl:with-param name="search" select="' '"/>
        <xsl:with-param name="replace" select="'%20'"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="replace">
    <xsl:param name="text"/>
    <xsl:param name="search"/>
    <xsl:param name="replace"/>
    <xsl:choose>
        <xsl:when test="contains($text, $search)">
            <xsl:variable name="replace-next">
                <xsl:call-template name="replace">
                    <xsl:with-param name="text" select="substring-after($text, $search)"/>
                    <xsl:with-param name="search" select="$search"/>
                    <xsl:with-param name="replace" select="$replace"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:value-of 
                select="
                    concat(
                        substring-before($text, $search)
                    ,   $replace
                    ,   $replace-next
                    )
                "
            />
        </xsl:when>
        <xsl:otherwise><xsl:value-of select="$text"/></xsl:otherwise>
    </xsl:choose>
</xsl:template>

编辑:将 match="text()" 更改为 match="text()[not(../*)]",这样输入的 xml 就不必是一种“漂亮的打印 XML” ..(以便在此类 xml 文件中使用“%20”字符串删除不需要的空格替换)

于 2010-02-23T12:42:59.963 回答
2

“prety-printed xml”的解决方案并不是真正的解决方案。

想象一下有这样的文档:

<a>
 <b>
  <c>O M G</c>
  <d>D I Y</d>
 </b>
</a>

当前接受的解决方案的输出(将其包装在 an 中<xsl:stylesheet>并添加身份规则后为:

<a>
%20<b>
%20%20<c>O$M$G</c>
%20%20<d>D$I$Y</d>
%20</b>
</a>

现在,为什么建议的解决方法不能挽救这种情况?正如我们从上面的例子中看到的,一个元素可以有多个具有文本节点的子元素......

真正的解决方案是什么?

XSLT 的创建者已经考虑过这个问题。使用正确的术语,我们希望 XSLT 处理器忽略所有无意义的纯空白文本节点,就好像它们根本不是文档树的一部分。这是通过<xsl:strip-space>指令实现的。

只需在全局级别添加它(作为子级<xsl:stylesheet>,为了便于阅读,在任何模板之前):

 <xsl:strip-space elements="*"/>

现在你真的有了一个可行的解决方案。

于 2010-02-23T17:57:38.820 回答