Remplacer la chaîne XSLT

85

Je ne connais pas vraiment XSL mais j'ai besoin de corriger ce code, je l'ai réduit pour le rendre plus simple.
Je reçois cette erreur

Fonction XSLT / XPath non valide

sur cette ligne

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

C'est le 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>

Quelqu'un peut-il me dire ce qui ne va pas?

Aximili
la source
Veuillez noter que la replace()fonction est disponible à partir de XPath 2.0 (et donc XSLT 2.0) et prend en charge les remplacements d'expressions régulières.
Abel

Réponses:

147

replace n'est pas disponible pour XSLT 1.0.

Le codage a un modèle de remplacement de chaîne que vous pouvez utiliser comme substitut à la fonction:

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

invoqué comme:

<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'un autre côté, si vous n'avez littéralement besoin de remplacer qu'un caractère par un autre, vous pouvez appeler translatequi a une signature similaire. Quelque chose comme ça devrait fonctionner correctement:

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

Notez également que dans cet exemple, j'ai changé le nom de la variable en "newtext", dans XSLT les variables sont immuables, vous ne pouvez donc pas faire l'équivalent de ce $foo = $fooque vous aviez dans votre code d'origine.

Mark Elliot
la source
Merci Mark, mais maintenant
j'obtiens
@aximili, désolé, a confondu XSLT 1.0 et 2.0, édité ... devrait être prêt à partir maintenant.
Mark Elliot
19
Cette réponse est fausse! La fonction de remplacement dans XSLT remplace les caractères uniques correspondants, pas les chaînes entières! Voir par exemple ici: w3schools.com/xpath/xpath_functions.asp
Jakub
12
@Jakub Vous pensez translate, non replace. La replacefonction dans XPath 2.0 traite son deuxième argument comme une expression régulière et remplace toutes les correspondances de cette expression par la chaîne de remplacement spécifiée (qui peut inclure des $nréférences à des groupes de capture dans l' expression régulière ). La translatefonction (dans 1.0 et 2.0) est celle qui effectue les remplacements de caractère unique pour un caractère unique.
Ian Roberts
6
la 4ème ligne de l'exemple d'utilisation ne devrait-elle pas être <xsl:with-param name="replace" select="'a'" />entre guillemets autour du a?
DJL du
37

Voici la fonction XSLT qui fonctionnera de manière similaire à la fonction String.Replace () de C #.

Ce modèle a les 3 paramètres comme ci-dessous

texte : - votre chaîne principale

remplacer : - la chaîne que vous souhaitez remplacer

par : - la chaîne qui répondra par une nouvelle chaîne

Voici le modèle

<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'exemple ci-dessous montre comment l'appeler

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

Vous pouvez également consulter l' URL ci - dessous pour plus de détails.

Optimus
la source
1
Utilisation de xslt 1.0 Ce message / modèle a fonctionné pour moi, contrairement à celui de Mark Elliot.
HostMyBus
12

Remarque: Si vous souhaitez utiliser l'algo déjà mentionné pour les cas où vous devez remplacer un grand nombre d'instances dans la chaîne source (par exemple, de nouvelles lignes dans du texte long), il y a une forte probabilité que vous vous retrouviez avec à StackOverflowExceptioncause de la récursivité appel.

J'ai résolu ce problème grâce à l' incorporation de type Java intégrée de Xalan ( je n'ai pas regardé comment le faire dans 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ć
la source
Désolé si je suis stupide mais je reçois:Cannot find a script or an extension object associated with namespace 'xalan://java.lang.String'.
Ian Grainger
Quel est votre moteur XSLT?
Milan Aleksić
3
Mon commentaire était pour le moteur Java XSLT 1.0 le plus populaire Xalan ( xml.apache.org/xalan-j ), qui prend en charge le mappage direct vers les types disponibles dans le chemin de classe Java disponible; vous ne pouvez pas appliquer ma solution pour .Net stack
Milan Aleksić
@IanGrainger, vous pouvez l'utiliser avec .NET en ajoutant un <msxsl:script>bloc, qui peut appeler n'importe quelle méthode .NET, bibliothèque, etc.
Abel
exslt est également supporté dans libxslt et donc dans tous les descendants xsltproc etc ...
Alain Pannetier
7

Vous pouvez utiliser le code suivant lorsque votre processeur s'exécute sur .NET ou utilise MSXML (par opposition à Java ou à d'autres processeurs natifs). Il utilise msxsl:script.

Assurez-vous d'ajouter l'espace xmlns:msxsl="urn:schemas-microsoft-com:xslt"de noms à votre racine xsl:stylesheetou xsl:transformélément.

De plus, liez outletà n'importe quel espace de noms que vous aimez, par exemple 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
la source
Désolé si je suis stupide, mais j'obtiens prefix outlet is not definedou 'xsl:script' cannot be a child of the 'xsl:stylesheet' element.si je change msxsl pour mon préfixe. Je suppose que c'est une magie XSLT spécifique à Microsoft?
Ian Grainger
1
@IanGrainger, ce n'est pas le cas xsl:script, mais msxsl:script, et il a un espace de noms différent (j'ai mis à jour la réponse de John).
Abel
1

Je continue à frapper cette réponse. Mais aucun d'entre eux ne répertorie la solution la plus simple pour xsltproc (et probablement la plupart des processeurs XSLT 1.0):

  1. Ajoutez le nom des chaînes exslt à la feuille de style, c'est-à-dire:
<xsl:stylesheet
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  1. Ensuite, utilisez-le comme:
<xsl:value-of select="str:replace(., ' ', '')"/>
Berend de Boer
la source
1
Le xsltproc sur mon ordinateur (macOS 10.13) ne prend PAS en charge la str:replace()fonction. Aucun des autres processeurs XSLT 1.0 majeurs - Xalan, Saxon 6.5 et Microsoft ne le fait non plus.
michael.hor257k
0

La rouine est assez bonne, mais cela fait que mon application se bloque, alors j'ai dû ajouter le cas:

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

avant que la fonction ne soit appelée de manière récursive.

J'ai eu la réponse d'ici: lorsque le test est suspendu dans une boucle infinie

Merci!

Chesare
la source