Matthias: Fast identisches Kopieren von xml nach xml

Hallo,

ich habe eine Reihe von DocBook-xml-Dokumenten, die überarbeitet wurden. Dabei wurden revision-attribute verwendet. Da die Überarbeitung nun abgeschlossen ist, möchte ich alle revision-Attribute löschen, damit bei der nächsten Überarbeitung die Dateien nicht zu unübersichtlich werden.

Folgende Schritte soll das .xsl tun:

  • wenn die node weder @revision noch @revisionflag hat, 1:1 kopieren
  • wenn die node @revision='test' und @revisionflag='deleted' hat, nicht übernehmen
  • wenn die node @revision='test' und @revisionflag='added' hat, übernehmen ohne @revision und ohne @revisionflag

Das klappt auch soweit ganz gut, bis auf folgende Probleme:

  1. Ich habe im root-element xmlns:xlink="http://www.w3.org/1999/xlink" angegeben, dieses wird aber nicht im result.xml im root ausgegeben, stattdessen aber in jedem element aus dem Namensraum. Ich häte das aber gerne wieder nur einmal im root.

  2. nodes, die wegen @revisionflag='deleted' nicht ausgegeben werden, generieren eine Leerzeile. Wie kann ich diese unterdrücken?

  3. Bei der Transformation erhalte ich jede Menge warnings:
    Ambiguous rule match for /processing-instruction()[1] Matches  both "processing-instruction()" on line 72 and "node()" on line 19
    Ambiguous rule match for /article[1]/text()[1] Matches both "text()" and "node()" on line 19

Ich bin schon ganz stolz soweit gekommen zu sein, da mir die xsl-Danke immer wieder Probleme bereitet, an den drei Punkten beisse ich mir aber nun die Zähne aus. Kann mir jemand einen Tip geben?

Vielen Dank.

Hier die Quelltexte:

removeRevisions.xml

<?xml version="1.0" encoding="UTF-8"?>  
<?oxygen RNGSchema="http://www.oasis-open.org/docbook/xml/5.0/rng/docbook.rng" type="xml"?>  
<article  
    version="5.0"  
    xmlns="http://docbook.org/ns/docbook"  
    xmlns:xlink="http://www.w3.org/1999/xlink">  
    <info>  
        <title>Test</title>  
    </info>  
    <para>Absatz 1.</para>  
    <para  
        revision="test"  
        revisionflag="deleted">Absatz 2.</para>  
    <para  
        revision="test"  
        revisionflag="added">Absatz 3.</para>  
    <para>Absatz 4.</para>  
    <para>Dies ist ein Absatz mit einem <link  
        xlink:href="http://www.google.de">Link</link> auf google.de.</para>  
</article>

removeRevisions.xsl

<?xml version="1.0" encoding="UTF-8"?>  
<xsl:stylesheet  
    version="1.0"  
    xmlns="http://docbook.org/ns/docbook"  
    xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"  
    xmlns:xlink="http://www.w3.org/1999/xlink"  
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  
  
  
  
    <xsl:template  
        match="/">  
        <xsl:apply-templates />  
    </xsl:template>  
  
  
  
    <xsl:template  
        match="node()">  
        <xsl:variable  
            name="check">  
            <xsl:choose>  
                <xsl:when  
                    test="attribute::revision = 'test'">  
                    <xsl:choose>  
                        <xsl:when  
                            test="attribute::revisionflag = 'added'">TRUE</xsl:when>  
                        <xsl:otherwise>FALSE</xsl:otherwise>  
                    </xsl:choose>  
                </xsl:when>  
                <xsl:otherwise>TRUE</xsl:otherwise>  
            </xsl:choose>  
        </xsl:variable>  
  
        <xsl:if  
            test="$check = 'TRUE'">  
            <xsl:element  
                name="{name()}">  
                <xsl:for-each  
                    select="attribute::*">  
                    <xsl:choose>  
                        <xsl:when  
                            test="name() = 'revision'" />  
                        <xsl:when  
                            test="name() = 'revisionflag'" />  
                        <xsl:otherwise>  
                            <xsl:attribute  
                                name="{name()}">  
                                <xsl:value-of  
                                    select="." />  
                            </xsl:attribute>  
                        </xsl:otherwise>  
                    </xsl:choose>  
                </xsl:for-each>  
                <xsl:apply-templates />  
            </xsl:element>  
        </xsl:if>  
    </xsl:template>  
  
  
  
    <xsl:template  
        match="text()">  
        <xsl:value-of  
            select="." />  
    </xsl:template>  
  
  
  
    <xsl:template  
        match="processing-instruction()">  
        <xsl:processing-instruction  
            name="{name()}">  
            <xsl:value-of  
                select="." />  
        </xsl:processing-instruction>  
    </xsl:template>  
  
  
  
</xsl:stylesheet>

result.xml

<?xml version="1.0" encoding="utf-8"?><?oxygen RNGSchema="http://www.oasis-open.org/docbook/xml/5.0/rng/docbook.rng" type="xml"?>  
<article  
    version="5.0"  
    xmlns="http://docbook.org/ns/docbook">  
    <info>  
        <title>Test</title>  
    </info>  
    <para>Absatz 1.</para>  
  
    <para>Absatz 3.</para>  
    <para>Absatz 4.</para>  
    <para>Dies ist ein Absatz mit einem <link  
        xlink:href="http://www.google.de"  
        xmlns:xlink="http://www.w3.org/1999/xlink">Link</link> auf google.de.</para>  
</article>
  1. Hallo Matthias,

    Ich bin schon ganz stolz soweit gekommen zu sein, da mir die xsl-Danke immer wieder Probleme bereitet, an den drei Punkten beisse ich mir aber nun die Zähne aus. Kann mir jemand einen Tip geben?

    Probiere es so:

    <?xml version="1.0" encoding="UTF-8"?>  
    <xsl:stylesheet version="1.0"  
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
      xmlns="http://docbook.org/ns/docbook"  
      xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"  
      xmlns:xlink="http://www.w3.org/1999/xlink">  
      
      <xsl:output method="xml" version="1.0" encoding="UTF-8"/>  
      
      <xsl:template match="@* | node()">  
        <xsl:copy>  
          <xsl:apply-templates select="@* | node()"/>  
        </xsl:copy>  
      </xsl:template>  
      
      <xsl:template match="*[@revision='test' and @revisionflag='deleted']"/>  
      
      <xsl:template match="@revision[.='test'] | @revisionflag[.='added']"/>  
      
      <xsl:template match="text()[normalize-space(.)='']"/>  
      
    </xsl:stylesheet>
    

    Das letzte Template vermeidet alle Whitespace-Knoten, im XML-Editor kann via "Pretty-Print"-Knopf die eingerückte Variante wiederhergestellt werden.

    Grüße,
    Thomas

    1. [Nachtrag:]

      Falls der XSLT-Prozessor indent="yes" unterstützt (etwa Saxon), wird die Ausgabe gleich wieder lesbar strukturiert erzeugt, also:

      <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

      Grüße,
      Thomas

      1. Hallo Thomas,

        vielen Dank, perfekt, tut genau das, was ich zu erreichen versucht habe. Und das mit viel weniger Zeilen.

        Was ich nicht ganz nachvollziehen kann ist, wie

        <xsl:template  
                match="@* | node()">  
                <xsl:copy>  
                    <xsl:apply-templates  
                        select="@* | node()" />  
                </xsl:copy>  
            </xsl:template>
        

        und

        <xsl:template  
                match="*[@revision='test' and @revisionflag='deleted']" />
        

        interagieren.

        Das | hängt alternative Ausdrücke aneinander, bedeutet also meinem Verständnis nach soviel wie 'oder'. Also in dem ersten Template "ein beliebiges Attribut" ODER "ein Element".
        Das zweite Template schliesst aber alle Elemente mit der angegebenen Attributs/Werte-Kombination aus.

        Wieso greift dieses zweite Template vor dem ersten?

        Hat das etwas mit dem nicht explizit angegebenen, und dadurch mit Standardwerten belegten, @priority des xsl:template zu tun wie http://de.selfhtml.org/xml/darstellung/xsltelemente.htm#template@title=hier beschrieben?
        Also

            <xsl:template  
                match="@* | node()" />
        ~~~ entspricht @priority=0.25 für Attribute und @priority="0" for Nodes.  
        ~~~xml
            <xsl:template  
                match="*[@revision='test' and @revisionflag='deleted']" />
        ~~~ entspricht @priority=0.25 für Nodes mit beiden Attributen/Werten  
        ~~~xml
            <xsl:template  
                match="@revision[.='test'] | @revisionflag[.='added']" />
        ~~~ entspricht @priority='0.5' für die spezifizierten Attribute.  
          
        Stimmt dies so?  
          
          
        Das ist genau das, was ich mit "die xsl-Denke macht mir Probleme" in meinem ersten Post gemeint habe, auf so eine Idee wäre ich nie gekommen.  
          
        Vielen Dank.  
          
        Gruß  
          
        Matthias  
          
        
        
        1. Hallo Matthias,

          Was ich nicht ganz nachvollziehen kann ist, wie

          <xsl:template

          match="@* | node()">
                  xsl:copy
                      <xsl:apply-templates
                          select="@* | node()" />
                  </xsl:copy>
              </xsl:template>

          
          > und  
          > ~~~xml
          
          <xsl:template  
          
          >         match="*[@revision='test' and @revisionflag='deleted']" />
          
          

          interagieren.

          Das erste ist ein Identitäts-Template, welches alle Knoten ausgibt. Die anderen Templates haben keinen Inhalt, machen also genau gar nichts mit den selektierten Elementknoten, schließen diese also von der Verarbeitung aus.

          Grüße,
          Thomas