Zastąp ciąg XSLT

85

Naprawdę nie znam XSL, ale muszę poprawić ten kod, zmniejszyłem go, aby był prostszy.
Otrzymuję ten błąd

Nieprawidłowa funkcja XSLT / XPath

w tej linii

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

To jest plik 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>

Czy ktoś może mi powiedzieć, co w tym złego?

Aximili
źródło
Należy pamiętać, że replace()funkcja jest dostępna od XPath 2.0 (a zatem XSLT 2.0) i obsługuje zastępowanie wyrażeń regularnych.
Abel

Odpowiedzi:

147

replace nie jest dostępny dla XSLT 1.0.

Codesling ma szablon do zamiany ciągów znaków, którego możesz użyć jako substytutu dla funkcji:

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

powołany jako:

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

Z drugiej strony, jeśli potrzebujesz dosłownie tylko zamienić jeden znak na inny, możesz zadzwonić, translatektóry ma podobny podpis. Coś takiego powinno działać dobrze:

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

Zwróć też uwagę, że w tym przykładzie zmieniłem nazwę zmiennej na "nowy tekst", w XSLT zmienne są niezmienne, więc nie możesz zrobić odpowiednika tego $foo = $foo, co miałeś w oryginalnym kodzie.

Mark Elliot
źródło
Dzięki Mark, ale teraz
pojawia
@aximili, przepraszam, pomyliłem XSLT 1.0 i 2.0, wyedytowałem ... teraz powinno być dobrze.
Mark Elliot
19
Ta odpowiedź jest błędna! Funkcja replace w XSLT zastępuje odpowiednie POJEDYNCZE ZNAKI, a nie całe ciągi! Zobacz na przykład tutaj: w3schools.com/xpath/xpath_functions.asp
Jakub
12
@Jakub Nie myślisz o translatetym replace. replaceFunkcja w XPath 2.0 traktuje jej jako drugi argument jest wyrażeniem regularnym i zastępuje wszystkie mecze tego wyrażenia z określonym ciągiem zastępczym (które mogą zawierać $nodniesienia do grup przechwytywania w regex). translateFunkcji (w 1,0 do 2,0), to taki, który ma postać pojedynczej-for-jednoznakowe wymiany.
Ian Roberts
6
czy czwarta linia w przykładzie użycia nie powinna zawierać <xsl:with-param name="replace" select="'a'" />cudzysłowów wokół a?
DJL
37

Oto funkcja XSLT, która będzie działać podobnie do funkcji String.Replace () języka C #.

Ten szablon ma 3 parametry, jak poniżej

tekst : - twój główny ciąg

replace : - ciąg, który chcesz zamienić

by : - ciąg, który odpowie nowym napisem

Poniżej znajduje się szablon

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

Poniższy przykład pokazuje, jak to nazwać

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

Aby uzyskać szczegółowe informacje, możesz również zapoznać się z poniższym adresem URL .

Optimus
źródło
1
Korzystanie z xslt 1.0 Ten post / szablon działał dla mnie, podczas gdy Mark Elliot nie.
HostMyBus,
12

Uwaga: jeśli chcesz użyć wspomnianego już algo w przypadkach, w których musisz zamienić dużą liczbę wystąpień w ciągu źródłowym (np. Nowe wiersze w długim tekście), istnieje duże prawdopodobieństwo, że skończysz z StackOverflowExceptionpowodu rekurencyjnego połączenie.

Rozwiązałem ten problem dzięki wbudowanemu osadzaniu typu Java przez Xalana (nie wyglądałem, jak to zrobić po Saksonii ):

<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ć
źródło
Przepraszam, jeśli jestem głupi, ale otrzymuję:Cannot find a script or an extension object associated with namespace 'xalan://java.lang.String'.
Ian Grainger
Jaki jest Twój silnik XSLT?
Milan Aleksić
3
Mój komentarz dotyczył najpopularniejszego silnika Java XSLT 1.0 Xalan ( xml.apache.org/xalan-j ), który obsługuje bezpośrednie mapowanie na dostępne typy w dostępnej ścieżce klas Javy; nie możesz zastosować mojego rozwiązania dla stosu .Net
Milan Aleksić
@IanGrainger, możesz go używać z .NET przez dodanie <msxsl:script>bloku, który może wywołać dowolną metodę .NET, bibliotekę itp. Chociaż .NET obsługuje również funkcje rozszerzenia EXSLT, więc nie musisz tego robić.
Abel
exslt jest również obsługiwany w libxslt, a zatem we wszystkich potomkach xsltproc itp ...
Alain Pannetier
7

Poniższego kodu można użyć, gdy procesor działa w środowisku .NET lub używa MSXML (w przeciwieństwie do procesorów opartych na języku Java lub innych natywnych). Używa msxsl:script.

Pamiętaj, aby dodać przestrzeń nazw xmlns:msxsl="urn:schemas-microsoft-com:xslt"do katalogu głównego xsl:stylesheetlub xsl:transformelementu.

Ponadto powiąż outletna przykład z dowolną przestrzenią nazw 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
źródło
Przepraszam, jeśli jestem głupi, ale otrzymuję prefix outlet is not definedlub 'xsl:script' cannot be a child of the 'xsl:stylesheet' element.zmieniam msxsl na mój prefiks. Zgaduję, że to jakaś magia XSLT specyficzna dla Microsoft?
Ian Grainger
1
@IanGrainger, to nie jest xsl:script, ale msxsl:scripti ma inną przestrzeń nazw (zaktualizowałem odpowiedź Johna).
Abel
1

Ciągle trafiam w tę odpowiedź. Ale żaden z nich nie wymienia najłatwiejszego rozwiązania dla xsltproc (i prawdopodobnie większości procesorów XSLT 1.0):

  1. Dodaj nazwę ciągów exslt do arkusza stylów, tj .:
<xsl:stylesheet
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  1. Następnie użyj go tak:
<xsl:value-of select="str:replace(., ' ', '')"/>
Berend de Boer
źródło
1
Xsltproc na moim komputerze (macOS 10.13) NIE obsługuje tej str:replace()funkcji. Ani żaden z pozostałych głównych procesorów XSLT 1.0 - Xalan, Saxon 6.5 i Microsoft.
michael.hor257k
0

Rouine jest całkiem niezły, jednak powoduje zawieszanie się mojej aplikacji, więc musiałem dodać skrzynkę:

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

zanim funkcja zostanie wywołana rekurencyjnie.

Mam stąd odpowiedź: podczas testu wisi w nieskończonej pętli

Dziękuję Ci!

Chesare
źródło