(xslt 1.0)如何用xml中的所有文本值替换一些字符串的空格?

时间:2010-02-23 12:40:51

标签: xml xslt xslt-1.0

  

编辑: [从字符替换开始   我最终发现了字符串   在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>
  

翻译(。,'','$')函数有效..但不是令人满意的程度..我的问题是..如果它是一个字符串怎么办   而不是性格?我的意思是,假设   我打算用'替换''   “20%”?还有一个案例,如果是的话   输入XML不是“Pretty Print XML”,   那么所有的空间都出现在XML中   被替换为'$'..

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

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

你可以观察到,<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,我真的很感激   感谢你们..

     

问候,新生儿。

3 个答案:

答案 0 :(得分: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处理器一起使用吗?

在另一个问题中问我,我可能会告诉你:)

答案 1 :(得分: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”字符串)

答案 2 :(得分:1)

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

想象一下这样的文档:

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

当前接受的解决方案的输出(在将其包装在<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="*"/>

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