XSLT字符串替换


85

我不太了解XSL,但是我需要修复此代码,为了简化起见,我对其进行了缩小。
我收到此错误

无效的XSLT / XPath函数

在这条线上

<xsl:variable name="text" select="replace($text,'a','b')"/>

这是XSL

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:inm="http://www.inmagic.com/webpublisher/query" version="1.0">
    <xsl:output method="text" encoding="UTF-8" />

    <xsl:preserve-space elements="*" />
    <xsl:template match="text()" />

    <xsl:template match="mos">
        <xsl:apply-templates />

        <xsl:for-each select="mosObj">
          'Notes or subject' 
           <xsl:call-template
                name="rem-html">
                <xsl:with-param name="text" select="SBS_ABSTRACT" />
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="rem-html">
        <xsl:param name="text" />
        <xsl:variable name="text" select="replace($text, 'a', 'b')" />
    </xsl:template>
</xsl:stylesheet>

谁能告诉我这是怎么回事?


请注意,此replace()函数可从XPath 2.0(因此也称为XSLT 2.0)开始使用,并支持正则表达式替换。
亚伯

Answers:


147

replace 不适用于XSLT 1.0。

Codesling有一个用于字符串替换模板,您可以用它代替该函数:

<xsl:template name="string-replace-all">
    <xsl:param name="text" />
    <xsl:param name="replace" />
    <xsl:param name="by" />
    <xsl:choose>
        <xsl:when test="$text = '' or $replace = ''or not($replace)" >
            <!-- Prevent this routine from hanging -->
            <xsl:value-of select="$text" />
        </xsl:when>
        <xsl:when test="contains($text, $replace)">
            <xsl:value-of select="substring-before($text,$replace)" />
            <xsl:value-of select="$by" />
            <xsl:call-template name="string-replace-all">
                <xsl:with-param name="text" select="substring-after($text,$replace)" />
                <xsl:with-param name="replace" select="$replace" />
                <xsl:with-param name="by" select="$by" />
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

调用为:

<xsl:variable name="newtext">
    <xsl:call-template name="string-replace-all">
        <xsl:with-param name="text" select="$text" />
        <xsl:with-param name="replace" select="a" />
        <xsl:with-param name="by" select="b" />
    </xsl:call-template>
</xsl:variable>

另一方面,如果您只需要用一个字符替换另一个字符,则可以调用translate具有相似签名的字符。这样的事情应该可以正常工作:

<xsl:variable name="newtext" select="translate($text,'a','b')"/>

另外,请注意,在此示例中,我将变量名称更改为“ newtext”,因为XSLT中的变量是不可变的,所以您不能做与$foo = $foo原始代码中相同的操作。


谢谢Mark,但是现在我遇到了这个错误:一个未知的XPath扩展函数被调用
Aximili 2010年

@aximili,对不起,让XSLT 1.0和2.0感到困惑,编辑...现在应该很好。
Mark Elliot 2010年

19
这个答案是错误的!XSLT中的replace函数将替换相应的SINGLE CHARACTERS,而不是整个字符串!参见示例:w3schools.com/xpath/xpath_functions.asp
Jakub

12
@Jakub你在想translate,不是replacereplaceXPath 2.0中的函数将其第二个参数视为正则表达式,并用指定的替换字符串(可能包括$n对正则表达式中捕获组的引用)替换该表达式的所有匹配项。该translate函数(在1.0和2.0中)是执行单字符到单字符替换的函数。
伊恩·罗伯茨

6
示例用法中的第四行不应该在a前后 <xsl:with-param name="replace" select="'a'" />加上引号吗?
DJL 2013年

37

这是XSLT函数,其功能类似于C#的String.Replace()函数。

该模板具有以下3个参数

文字:-您的主要字串

replace:-您要替换的字符串

通过:-将由新字符串答复的字符串

以下是模板

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

下面的示例显示了如何调用它

<xsl:variable name="myVariable ">
  <xsl:call-template name="string-replace-all">
    <xsl:with-param name="text" select="'This is a {old} text'" />
    <xsl:with-param name="replace" select="'{old}'" />
    <xsl:with-param name="by" select="'New'" />
  </xsl:call-template>
</xsl:variable>

您也可以参考下面的URL了解详细信息。


1
使用xslt 1.0这篇文章/模板对我有用,而Mark Elliot的则不是。
HostMyBus '16

12

注意:如果你想使用算法中已经提到的,你需要替换源字符串中的情况下(在长文本如新线)的数量庞大的情况下,有较高的概率你会结束了StackOverflowException,因为递归呼叫。

我解决了这个问题,这要归功于Xalan的内置Java类型嵌入(在Saxon中不怎么做):

<xsl:stylesheet version="1.0" exclude-result-prefixes="xalan str"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xalan="http://xml.apache.org/xalan"
                xmlns:str="xalan://java.lang.String"
        >
...
<xsl:value-of select="str:replaceAll(
    str:new(text()),
    $search_string,
    $replace_string)"/>
...
</xsl:stylesheet>

抱歉,如果我很笨,但我得到:Cannot find a script or an extension object associated with namespace 'xalan://java.lang.String'.
Ian Grainger


3
我的评论是针对最流行的Java XSLT 1.0引擎Xalan(xml.apache.org/xalan-j)的,它支持直接映射到可用Java类路径中的可用类型。您无法将我的解决方案应用于.Net堆栈
MilanAleksić14年

@IanGrainger,您可以通过添加一个<msxsl:script>块来与.NET一起使用,该块可以调用任何.NET方法,库等。尽管.NET还支持EXSLT扩展功能,所以您不需要。
亚伯

libxslt还支持exslt,因此所有后代xsltproc等也都支持exslt
Alain Pannetier

7

当处理器在.NET上运行或使用MSXML(而不是基于Java的处理器或其他本机处理器)时,可以使用以下代码。它使用msxsl:script

确保将名称空间添加xmlns:msxsl="urn:schemas-microsoft-com:xslt"到您的根xsl:stylesheetxsl:transform元素。

另外,绑定outlet到任何您喜欢的名称空间,例如xmlns:outlet = "http://my.functions"

<msxsl:script implements-prefix="outlet" language="javascript">
function replace_str(str_text,str_replace,str_by)
{
     return str_text.replace(str_replace,str_by);
}
</msxsl:script>


<xsl:variable name="newtext" select="outlet:replace_str(string(@oldstring),'me','you')" />

抱歉,如果我很笨,但是我得到了,prefix outlet is not defined或者'xsl:script' cannot be a child of the 'xsl:stylesheet' element.我更改了msxsl作为前缀。我猜这是Microsoft特定的XSLT魔术吗?
伊恩·格兰杰

1
@IanGrainger,不是xsl:script,而是msxsl:script,并且具有不同的命名空间(我已经更新了John的答案)。
亚伯

1

我一直在寻找答案。但是它们都没有列出xsltproc(可能是大多数XSLT 1.0处理器)最简单的解决方案:

  1. 将exslt字符串名称添加到样式表中,即:
<xsl:stylesheet
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  1. 然后像这样使用它:
<xsl:value-of select="str:replace(., ' ', '')"/>

1
我的计算机(macOS 10.13)上的xsltproc不支持该str:replace()功能。其他主要的XSLT 1.0处理器(Xalan,Saxon 6.5和Microsoft)也没有。
michael.hor257k

0

例程非常好,但是它导致我的应用程序挂起,因此我需要添加大小写:

  <xsl:when test="$text = '' or $replace = ''or not($replace)" >
    <xsl:value-of select="$text" />
    <!-- Prevent thsi routine from hanging -->
  </xsl:when>

在递归调用函数之前。

我从这里得到答案: 当测试陷入无限循环时

谢谢!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.