Substituição de string XSLT

86

Eu realmente não sei XSL, mas preciso corrigir este código, eu o reduzi para torná-lo mais simples.
Estou recebendo este erro

Função XSLT / XPath inválida

nesta linha

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

Este é o 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>

Alguém pode me dizer o que há de errado com isso?

Aximili
fonte
Observe que a replace()função está disponível a partir do XPath 2.0 (e, portanto, do XSLT 2.0) e suporta substituições de expressões regulares.
Abel

Respostas:

150

replace não está disponível para XSLT 1.0.

Codesling tem um modelo para substituição de string que você pode usar como um substituto para a função:

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

invocado como:

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

Por outro lado, se você literalmente precisar apenas substituir um caractere por outro, pode chamar o translateque tem uma assinatura semelhante. Algo assim deve funcionar bem:

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

Além disso, observe que, neste exemplo, mudei o nome da variável para "newtext", em XSLT as variáveis ​​são imutáveis, então você não pode fazer o equivalente ao $foo = $fooque tinha no código original.

Mark Elliot
fonte
Obrigado, Mark, mas agora estou recebendo este erro: Uma função de extensão XPath desconhecida foi chamada
Aximili
@aximili, desculpe, tenho XSLT 1.0 e 2.0 confuso, editado ... deve estar pronto para ir agora.
Mark Elliot
19
Essa resposta está errada! A função de substituição em XSLT substitui os CARACTERES ÚNICOS correspondentes, não as strings inteiras! Veja por exemplo aqui: w3schools.com/xpath/xpath_functions.asp
Jakub
12
@Jakub Você está pensando translate, não replace. A replacefunção no XPath 2.0 trata seu segundo argumento como uma expressão regular e substitui todas as correspondências dessa expressão pela string de substituição especificada (que pode incluir $nreferências a grupos de captura na regex). A translatefunção (em 1.0 e 2.0) é aquela que faz substituições de caractere único por caractere único.
Ian Roberts,
6
a 4ª linha no exemplo de uso não deveria estar <xsl:with-param name="replace" select="'a'" />entre aspas em torno de a?
DJL
37

Aqui está a função XSLT que funcionará de forma semelhante à função String.Replace () do C #.

Este modelo tem os 3 parâmetros abaixo

text : - sua string principal

substituir : - a string que você deseja substituir

por : - a string que responderá por uma nova string

Abaixo estão o modelo

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

O exemplo abaixo mostra como chamá-lo

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

Você também pode consultar o URL abaixo para obter os detalhes.

Optimus
fonte
1
Usando xslt 1.0 Este post / template funcionou para mim, enquanto o de Mark Elliot não.
HostMyBus
12

Nota: No caso de você desejar usar o algoritmo já mencionado para casos em que você precisa substituir um grande número de instâncias na string de origem (por exemplo, novas linhas em texto longo), há alta probabilidade de você acabar com StackOverflowExceptionpor causa da recursiva ligar.

Resolvi esse problema graças à incorporação de tipo Java integrada de Xalan (não parecia como fazer no 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>
Milan Aleksić
fonte
Desculpe se estou sendo burro, mas entendi:Cannot find a script or an extension object associated with namespace 'xalan://java.lang.String'.
Ian Grainger
Qual é o seu motor XSLT?
Milan Aleksić
3
Meu comentário foi sobre o mecanismo Xalan mais popular do Java XSLT 1.0 ( xml.apache.org/xalan-j ), que oferece suporte ao mapeamento direto para tipos disponíveis dentro do classpath Java disponível; você não pode aplicar minha solução para .Net stack
Milan Aleksić
@IanGrainger, você pode usá-lo com o .NET adicionando um <msxsl:script>bloco, que pode chamar qualquer método .NET, biblioteca etc. Embora o .NET também suporte as funções de extensão EXSLT, você não precisa fazer isso.
Abel
exslt também é compatível com libxslt e, portanto, em todos os descendentes xsltproc etc ...
Alain Pannetier
7

Você pode usar o código a seguir quando seu processador é executado em .NET ou usa MSXML (ao contrário de processadores baseados em Java ou outros processadores nativos). Ele usa msxsl:script.

Certifique-se de adicionar o namespace xmlns:msxsl="urn:schemas-microsoft-com:xslt"à sua raiz xsl:stylesheetou xsl:transformelemento.

Além disso, vincule outlet- se a qualquer namespace de sua preferência, por exemplo 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')" />
John Jin
fonte
Desculpe se estou sendo burro, mas entendi prefix outlet is not definedou 'xsl:script' cannot be a child of the 'xsl:stylesheet' element.se mudei o msxsl pelo meu prefixo. Suponho que seja alguma magia XSLT específica da Microsoft?
Ian Grainger
1
@IanGrainger, não é xsl:script, mas msxsl:script, e tem um namespace diferente (atualizei a resposta de John).
Abel
1

Eu continuo acertando essa resposta. Mas nenhum deles lista a solução mais fácil para xsltproc (e provavelmente para a maioria dos processadores XSLT 1.0):

  1. Adicione o nome das strings exslt à folha de estilo, ou seja:
<xsl:stylesheet
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  1. Em seguida, use-o como:
<xsl:value-of select="str:replace(., ' ', '')"/>
Berend de Boer
fonte
1
O xsltproc no meu computador (macOS 10.13) NÃO suporta a str:replace()função. Nem qualquer um dos outros principais processadores XSLT 1.0 - Xalan, Saxon 6.5 e Microsoft.
michael.hor257k
0

A rouina é muito boa, mas faz com que meu aplicativo trave, então precisei adicionar o caso:

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

antes que a função seja chamada recursivamente.

Eu obtive a resposta aqui: Quando o teste está suspenso em um loop infinito

Obrigado!

Chesare
fonte