Sostituzione stringa XSLT


86

Non conosco davvero XSL ma ho bisogno di correggere questo codice, l'ho ridotto per renderlo più semplice.
Ricevo questo errore

Funzione XSLT / XPath non valida

su questa linea

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

Questo è l'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>

Qualcuno può dirmi cosa c'è che non va?


Si noti che la replace()funzione è disponibile da XPath 2.0 (e quindi XSLT 2.0) in poi e supporta le sostituzioni di espressioni regolari.
Abel

Risposte:


150

replace non è disponibile per XSLT 1.0.

Codesling ha un modello per la sostituzione delle stringhe che puoi usare come sostituto della funzione:

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

invocato come:

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

D'altra parte, se hai letteralmente solo bisogno di sostituire un carattere con un altro, puoi chiamare translateche ha una firma simile. Qualcosa di simile dovrebbe funzionare bene:

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

Inoltre, nota, in questo esempio, ho cambiato il nome della variabile in "newtext", in XSLT le variabili sono immutabili, quindi non puoi fare l'equivalente di $foo = $foocome avevi nel codice originale.


Grazie Mark, ma ora ricevo questo errore: è stata chiamata una funzione di estensione XPath sconosciuta
Aximili

@aximili, scusa, ho confuso XSLT 1.0 e 2.0, modificato ... dovrebbe essere pronto ora.
Mark Elliot

19
Questa risposta è sbagliata! La funzione di sostituzione in XSLT sostituisce i CARATTERI SINGOLI corrispondenti, non le stringhe intere! Vedi ad esempio qui: w3schools.com/xpath/xpath_functions.asp
Jakub

12
@Jakub Stai pensando translate, no replace. La replacefunzione in XPath 2.0 tratta il suo secondo argomento come un'espressione regolare e sostituisce tutte le corrispondenze di tale espressione con la stringa di sostituzione specificata (che può includere $nriferimenti a gruppi di acquisizione nella regex). La translatefunzione (in 1.0 e 2.0) è quella che esegue la sostituzione di un carattere per un carattere singolo.
Ian Roberts

6
La quarta riga nell'esempio di utilizzo non dovrebbe essere <xsl:with-param name="replace" select="'a'" />racchiusa tra virgolette attorno alla a?
DJL

37

Ecco la funzione XSLT che funzionerà in modo simile alla funzione String.Replace () di C #.

Questo modello ha i 3 parametri come di seguito

testo : - la tua stringa principale

sostituire : - la stringa che si desidera sostituire

by : - la stringa che risponderà con una nuova stringa

Di seguito sono riportati i modelli

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

L'esempio seguente mostra come chiamarlo

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

Puoi anche fare riferimento all'URL seguente per i dettagli.


1
Usare xslt 1.0 Questo post / modello ha funzionato per me mentre quello di Mark Elliot no.
HostMyBus

12

Nota: nel caso in cui desideri utilizzare l'algoritmo già menzionato per i casi in cui devi sostituire un numero enorme di istanze nella stringa di origine (ad esempio nuove righe nel testo lungo) c'è un'alta probabilità che ti ritroverai a StackOverflowExceptioncausa della ricorsività chiamata.

Ho risolto questo problema grazie all'incorporamento di tipo Java integrato di Xalan (non ho visto come farlo in 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>

Scusa se sono stupido ma ottengo:Cannot find a script or an extension object associated with namespace 'xalan://java.lang.String'.
Ian Grainger

Qual è il tuo motore XSLT?
Milan Aleksić

3
Il mio commento era per il più popolare motore Java XSLT 1.0 Xalan ( xml.apache.org/xalan-j ), che supporta la mappatura diretta ai tipi disponibili all'interno del classpath Java disponibile; non puoi applicare la mia soluzione per lo stack .Net
Milan Aleksić

@IanGrainger, puoi usarlo con .NET aggiungendo un <msxsl:script>blocco, che può chiamare qualsiasi metodo .NET, libreria ecc. Sebbene .NET supporti anche le funzioni di estensione EXSLT, quindi non ne avrai bisogno.
Abel

exslt è supportato anche in libxslt e quindi in tutti i discendenti xsltproc ecc ...
Alain Pannetier

7

È possibile utilizzare il codice seguente quando il processore viene eseguito su .NET o utilizza MSXML (al contrario di processori basati su Java o altri processori nativi). Usa msxsl:script.

Assicurati di aggiungere lo spazio dei nomi xmlns:msxsl="urn:schemas-microsoft-com:xslt"alla radice xsl:stylesheeto xsl:transformall'elemento.

Inoltre, associa outleta qualsiasi spazio dei nomi che ti piace, ad esempio 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')" />

Scusa se sono stupido, ma ottengo prefix outlet is not definedo 'xsl:script' cannot be a child of the 'xsl:stylesheet' element.se cambio msxsl per il mio prefisso. Immagino che questa sia una magia XSLT specifica di Microsoft?
Ian Grainger

1
@IanGrainger, non lo è xsl:script, ma msxsl:script, e ha uno spazio dei nomi diverso (ho aggiornato la risposta di John).
Abel

1

Continuo a rispondere a questa risposta. Ma nessuno di loro elenca la soluzione più semplice per xsltproc (e probabilmente la maggior parte dei processori XSLT 1.0):

  1. Aggiungi il nome delle stringhe exslt al foglio di stile, ad esempio:
<xsl:stylesheet
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  1. Quindi usalo come:
<xsl:value-of select="str:replace(., ' ', '')"/>

1
Xsltproc sul mio computer (macOS 10.13) NON supporta la str:replace()funzione. Nemmeno gli altri principali processori XSLT 1.0: Xalan, Saxon 6.5 e Microsoft.
michael.hor257k

0

La rouine è abbastanza buona, tuttavia causa il blocco della mia app, quindi ho dovuto aggiungere la custodia:

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

prima che la funzione venga chiamata ricorsivamente.

Ho ottenuto la risposta da qui: quando il test è sospeso in un ciclo infinito

Grazie!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.