Announcement

Collapse
No announcement yet.

XSLT 2.0: Mehrfaches Replace() ?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • XSLT 2.0: Mehrfaches Replace() ?

    Ich habe folgende Beispiel-XML-Datei:

    Code:
    <text>
    Das ist der Text: ABC - beliebiger weiterer Text - XYZ
    </text>
    und möchte ABC durch DEF und XYZ durch UVW ersetzen.

    Folgender XSLT-Code:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
            <xsl:output method="xml"/>
            <xsl:template match="text">
                   <text>
                           <xsl:value-of select="replace(.,'ABC','DEF')"/>
                           <xsl:value-of select="replace(.,'XYZ','UVW')"/>
                   </text>
            </xsl:template>
    </xsl:stylesheet>
    bringt dieses Ergebnis:

    Code:
    <text>
    Das ist der Text: DEF - beliebiger weiterer Text - XYZ
    Das ist der Text: ABC - beliebiger weiterer Text - UVW
    </text>
    Das heißt, der Taginhalt wird zweimal ersetzt und zweimal ausgegeben, einmal wird nur die erste Ersetzung durchgeführt, einmal nur die zweite...
    Wie kann ich es am besten lösen, dass beide Ersetzungen durchgeführt und als ein Inhalt ausgegeben werden?


    Eine Möglichkeit wäre, das Suchergebnis in eine Variable zu speichern und diese Variable für die nächste Suche zu verwenden:

    Code:
            <xsl:template match="text">
                   <xsl:variable name="var1">
                           <xsl:value-of select="replace(.,'ABC','DEF')"/>
                   </xsl:variable>
                   <text>
                           <xsl:value-of select="replace($var1,'XYZ','UVW')"/>                  
                   </text>
            </xsl:template>

    So würde es auch gehen:

    Code:
    <xsl:value-of select="replace(replace(.,'XYZ','UVW'),'ABC','DEF')"/>

    Ist aber beides nicht unbedingt schön, vor allem, wenn man bedenkt, dass ich in Wirklichkeit Dutzende Ersetzungen durchführen muss.
    Kann mir jemand eine bessere Lösung vorschlagen? Oder ist es generell besser, solche Such-/Ersetz-Vorgänge nicht mit XSLT, sondern
    vorher oder nachher mit anderen Werkzeugen zu erledigen?

    Gruß, Peter

  • #2
    Entweder eine eigene XSLT-Funktion schreiben (mittels xsl:function) oder xsb:replace() aus der XSLT-Standard-Bibliothek (XSLT-SB) von Stefan Krause einsetzen.

    Comment


    • #3
      Danke für die rasche Antwort!

      Werde ich gleich testen!

      Gruß, Peter

      Comment


      • #4
        Das mehrfache Ersetzen klappt einmal toll!

        Ich habe leider noch eine Frage:

        Bei folgender Beispiel-XML-Datei
        Code:
        <text>
        Das ist der <b>fette</b> Text: ABC XYZ
        </text>
        gibt es im Element <text> aber auch das Unterelement <b>. Mit diesem Code

        Code:
        <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsb="http://www.expedimentum.org/XSLT/SB" exclude-result-prefixes="xs xsb">
        	<xsl:import href="xsb_strings.xsl"/>
        	<xsl:output method="xml"/>
        	<xsl:template match="text">
        		<text2>
        			<xsl:value-of select="xsb:replace(.,
        			('ABC', 'XYZ'),
        			('DEF', 'UVW')   )"/>
        		</text2>
        	</xsl:template>
        </xsl:stylesheet>
        wird das Element <b> aber ignoriert und nicht ausgegeben.

        Ich glaube, irgendwo muss es noch "klick" machen, damit ich die XSLT-Logik verstehe...

        Comment


        • #5
          Benutze
          Code:
          <xsl:template match="text">
            <text2>
              <xsl:apply-templates/>
            </text2>
          </xsl:template>
          
          <xsl:template match="text//text()">
            <xsl:value-of select="xsb:replace(., ('ABC', 'XYZ'), ('DEF', 'UVW'))"/>
          </xsl:template>
          So das "b"-Element in die Ausgabe kopiert werden soll, brauchst du noch ein Template
          Code:
          <xsl:template match="b">
            <xsl:copy>
               <xsl:apply-templates/>
            </xsl:copy>
          </xsl:template>
          so die Aufgabe nicht bereits von einem identity transformation template (siehe Beispiel in http://www.w3.org/TR/xslt20/#shallow-copy) übernommen wird.

          So das "b"-Element transformiert werden soll, also etwa in ein "d"-Element, dann halt per
          Code:
          <xsl:template match="b">
            <d>
               <xsl:apply-templates/>
            </d>
          </xsl:template>

          Comment


          • #6
            Danke für den Hinweis.
            Das mit dem weiteren Template für "b" bzw. mit dem Identity Transformation Template hatte ich vorher schon probiert. Das funktionierte mit <xsl:template match="text"> aber nicht.

            Das Zauberwort war aber: <xsl:template match="text//text()">. Durch die text()-Funktion funktioniert das Ersetzen und das Unterelement <b> wird trotzdem berücksichtigt.

            Comment


            • #7
              Ich habe noch grundsätzliche Fragen dazu:
              Was ist der Unterschied zwischen match="text/text()" und match="text//text()"?

              Wenn ich mit <xsl:template match="*/text()"> verschiedene generelle Ersetzungen für alle Elemente durchführen lasse und für ein Element aber spezielle Ersetzungen brauche,
              z.B. in <xsl:template match="bericht[@typ='SEMINAR']/text[@typ='Inhalt']/text()">, dann werden im betreffenden Element nur die speziellen Ersetzungen
              durchgeführt, nicht aber die generellen.
              Kann man in XSLT irgendwie definieren, dass das Template sozusagen zusätzlich ausgeführt wird und nicht statt dem anderen?

              Gruß, Peter

              Comment


              • #8
                Das Pattern "text/text()" wird auf Textknoten angewendet, die Kindknoten eines Elementes mit dem Namen "text" sind, also
                Code:
                <text>Dies ist ein Kindknoten</text>
                Das Pattern "text//text()" wird auf Textknoten angewendet, die Nachfahren eines Elementes mit dem Namen "text" sind, also "Kinder", "Enkel" und so weiter
                Code:
                <text>Dies ist ein direkter Nachfahre (Kind).<span>Dies ist ein Nachfahre zweiter Generation. (Enkel).</span><span><b>Und dies ist ein Nachfahre dritter Generation.</b></span></text>
                Das zusätzliche Ausführen eines Templates kann man in XSLT 2.0 (das du benutzt) sehr elegant per xsl: next-match erreichen, siehe http://www.w3.org/TR/xslt20/#element-next-match. Ansonsten gibt es noch apply-imports, so man Templates importiert, aber auch überschrieben hat, und dann im überschriebenen Template das importierte Template zusätzlich anwenden will. Und ansonsten kann man mit "modes" arbeiten http://www.w3.org/TR/xslt20/#modes, um einen Knoten mit mehreren Templates zu verarbeiten.

                Comment


                • #9
                  Wow! Vielen Dank für die raschen und so wertvollen Informationen...

                  Comment

                  Working...
                  X