【问题标题】:(xslt 1.0) How to replace the space with some string from all the text values in xml?(xslt 1.0)如何用xml中所有文本值中的一些字符串替换空格?
【发布时间】:2010-02-23 12:40:51
【问题描述】:

编辑: [从字符替换开始 最后我发现了字符串 在 Dimitre Novatchev 和 Roland 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 中的所有空间 替换为 '$' ..

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

多一个节点 这是@较低级别

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

如下例所示..

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

所有开始标签之前都有空格字符..&lt;child&gt;标签之前的空格比&lt;test2&gt;节点多..

使用第二个示例 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 Novatchev 和Roland Bouman 发布的解决方案也可以用另一个字符串替换一个字符串,通过 修改传递给 正在调用模板。

这是很好的学习@Dimitre, @Roland,我真的很感激 谢谢你们..

问候,
婴儿专业人士。

【问题讨论】:

  • 又是一个好问题。我不明白关于“Pretty Print XML”的那部分——请您详细说明一下吗?最好是另一个输入和输出示例。
  • 程序员:xml 是否在一行中并不重要——如果它只有空白节点,我的解决方案将删除所有节点。如果您所说的实际上没有纯空白文本节点,那么 xsl:strip-space 指令将什么也不做——在这种情况下它是无害的。我提供的解决方案更通用——它适用于两种情况:只有空白节点和没有空白节点时。

标签: xml xslt xslt-1.0


【解决方案1】:

按照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 处理器?

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

【讨论】:

  • Dimitre,感谢您提供此信息。不过,我还不清楚一件事。您说递归调用是模板中的最后一条指令。但是,如果我解析模板,在我看来有一个顶级指令,即&lt;xsl:choose&gt;。所以根据定义,不是&lt;xsl:call-template&gt;,而是&lt;xsl:choose&gt; 是最后一条指令。或者这仍然被认为是尾递归,因为&lt;xsl:choose&gt; 实际上并没有做任何事情,除了执行它的一个分支?
  • @Roland-Bouman,&lt;xsl:call-template&gt; 是最后一个 executable 指令——在它之后只有结束标签。一些足够聪明的 XSLT 处理器使用它来确定模板是尾递归的,然后执行优化以使用迭代代码。
  • Dimitre,谢谢。所以我想这回答了我之前向您提出的关于 XSLT 处理器是否以及如何“重写”以获得尾递归的问题。
【解决方案2】:

查看 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”字符串)

【讨论】:

  • np。令人惊讶的是,这样的功能在许多语言中都不可用,因此很容易监督它的存在。
  • ohk .. btw .. 如果它是字符串而不是字符怎么办?我的意思是,假设我打算用“%20”替换''?还有一种情况,如果输入的 XML 不是“Pretty Print XML”,那么 XML 中出现的所有空格都被替换为 '$' ..
  • 好吧,基本上,在这种情况下你会很难过 :) 你将不得不编写一个递归替换文本的“替换文本”模板。我将修改我的答案以说明如何。
  • 除了只处理空白节点之外,该解决方案基本上是正确的——请参阅我的答案以获得这部分问题的正确解决方案。此解决方案的一个小问题是它不是尾递归的,并且由于调用堆栈非常深,可能导致堆栈溢出。我将重构留给尾递归作为练习:)
  • Dimitre,感谢您指出这些问题。虽然我知道缺乏尾递归,但我真正感兴趣的是:您知道不同的 XSLT 引擎如何优化和重写以获得它吗?我的意思是,我不知道并且想学习。
【解决方案3】:

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

想象一下这样的文档:

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

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

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

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

真正的解决方案是什么?

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

只需在全局级别添加(作为&lt;xsl:stylesheet&gt; 的子级,并且为了便于阅读,在任何模板之前):

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

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

【讨论】:

  • @Dimitre,感谢您(以通常的方式)正确的方法 :-) 但是带空间的问题是,输出 xml 将写在一行中[在记事本或类似编辑器中打开时我们可以观察到这一点],但并不是真正的问题,因为这对 XML 编辑器无关紧要..
  • @infant-programmer:xml 是否在一行中并不重要——如果它只有空白节点,我的解决方案将删除所有这些节点。如果您所说的实际上没有纯空白文本节点,那么 xsl:strip-space 指令将什么也不做——在这种情况下它是无害的。我提供的解决方案更通用——它适用于两种情况:只有空白节点和没有空白节点时。
猜你喜欢
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 2012-01-31
  • 2011-11-23
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
相关资源
最近更新 更多