Dokumente mit identischem Schema vereinigen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie haben zwei oder mehrere identisch strukturierte Dokumente und wollen sie zu einem einzigen Dokument vereinigen.

Lösung

Wenn der Inhalt der Dokumente sich voneinander unterscheidet oder Sie sich keine Sorgen um Duplikate machen, dann ist die Lösung einfach:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:param name="doc2"/>
  <xsl:template match="/*">
    <xsl:copy>
      <xsl:copy-of select="* | document($doc2)/*/*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Falls zwischen den Eingabedokumenten Duplikate existieren, Sie jedoch wollen, dass das Ausgabedokument eindeutige Einträge enthält, können Sie die Duplikate mit Hilfe der im Rezept Doppelt auftretende Elemente ignorieren besprochenen Technik entfernen. Betrachten Sie die beiden folgenden Beispiele.

Beispiel: Dokument 1.

<people which="MeAndMyFriends">
  <person firstname="Sal" lastname="Mangano" age="38" height="5.75"/>
  <person firstname="Mike" lastname="Palmieri" age="28" height="5.10"/>
  <person firstname="Vito" lastname="Palmieri" age="38" height="6.0"/>
  <person firstname="Vinny" lastname="Mari" age="37" height="5.8"/>
</people>

Beispiel: Dokument 2.

<people which="MeAndMyCoWorkers">
  <person firstname="Sal" lastname="Mangano" age="38" height="5.75"/>
  <person firstname="Al" lastname="Zehtooney" age="33" height="5.3"/>
  <person firstname="Brad" lastname="York" age="38" height="6.0"/>
  <person firstname="Charles" lastname="Xavier" age="32" height="5.8"/>
</people>

Das folgende Stylesheet vereinigt und löscht doppelt auftretende Elemente mit xsl:sort und den exsl:node-set-Erweiterungen:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common">
  <xsl:import href="exsl.xsl" />
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:param name="doc2"/>
  <!-- Hier führen wir ein 'key'-Attribut ein, um das Entfernen von Duplikaten zu vereinfachen -->
  <xsl:variable name="all">
    <xsl:for-each select="/*/person | document($doc2)/*/person">
      <xsl:sort select="concat(@lastname,@firstname)"/>
      <person key="{concat(@lastname, @firstname)}">
        <xsl:copy-of select="@* | node( )" />
      </person>  
    </xsl:for-each>
  </xsl:variable>
  <xsl:template match="/">
    <people>
      <xsl:for-each select="exsl:node-set($all)/person[not(@key = preceding-sibling::person[1]/@key)]">
        <xsl:copy-of select="."/>
      </xsl:for-each>
    </people>
  </xsl:template>
</xsl:stylesheet>

Duplikate auf diese Weise zu entfernen bringt drei Nachteile mit sich. Erstens ändert es die Reihenfolge der Elemente, was unerwünscht sein könnte. Zweitens erfordert es die Verwendung der node-set-Erweiterung in XSLT 1.0. Drittens ist es nicht generisch in dem Sinne, dass Sie für jede Situation, in der Sie eine Vereinigung ohne Duplikate haben wollen, das gesamte Stylesheet umschreiben müssen.

xsl:key verwendet eine Methode, diesen Problemen zu begegnen:

<!-- Stylesheet: merge-simple-using-key.xslt -->
<!-- Importieren Sie dieses Stylesheet in ein anderes, das den Schlüssel definiert -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:merge="http:www.ora.com/XSLTCookbook/mnamespaces/merge">
  <xsl:param name="doc2"/>
  <xsl:template match="/*">
    <!--Kopiert das äußerste Element des Quelldokuments -->
    <xsl:copy>
      <!-- Für jedes Kind in der Quelle wird festgestellt, ob es in das Ziel kopiert werden soll. Grundlage dieser Entscheidung ist die Frage, ob es in dem anderen Dokument existiert. -->
      <xsl:for-each select="*">
        <!-- Aufruf eines Templates, das einen eindeutigen Schlüsselwert für dieses Element ermittelt. Es muss im einschließenden Stylesheet definiert sein. -->
        <xsl:variable name="key-value">
          <xsl:call-template name="merge:key-value"/>
        </xsl:variable>
        <xsl:variable name="element" select="."/>
        <!--Dieses for-each dient einfach dazu, den Kontext auf das zweite Dokument zu ändern -->
        <xsl:for-each select="document($doc2)/*">
          <!-- Der Schlüssel key wird als Mechanismus zum Testen, ob das Element im zweiten Dokument vorhanden ist, verwendet. Der Schlüssel muss durch das einschließende Stylesheet definiert sein. -->
          <xsl:if test="not(key('merge:key', $key-value))">
            <xsl:copy-of select="$element"/>
          </xsl:if>
        </xsl:for-each>
      </xsl:for-each>
      <!--Kopiert alle Elemente im zweiten Dokument -->
      <xsl:copy-of select="document($doc2)/*/*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Das folgende Stylesheet importiert das vorherige und definiert den Schlüssel sowie ein Template, um den Wert des Schlüssels zu beziehen:

<!-- Dieses Stylesheet definiert die Eindeutigkeit von Elementen bezüglich eines Schlüssels. -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:merge="http:www.ora.com/XSLTCookbook/mnamespaces/merge">
  <xsl:include href="merge-simple-using-key.xslt"/>
  <!--Eine Person wird eindeutig durch die Verkettung des Nachnamens mit dem Vornamen definiert -->
  <xsl:key name="merge:key" match="person" use="concat(@lastname,@firstname)"/>
  <xsl:output method="xml" indent="yes"/>
  <!-- Dieses Template bezieht den Schlüsselwert für ein Element -->
  <xsl:template name="merge:key-value">
    <xsl:value-of select="concat(@lastname,@firstname)"/>
  </xsl:template>
</xsl:stylesheet>

Eine zweite Methode zum Vereinigen und Entfernen von Duplikaten verwendet wertebasierte Mengenoperationen, die im Rezept Mengenoperationen auf Knotenmengen mit Hilfe von Wertsemantiken ausführen besprochen werden. Dieses Rezept präsentiert die Lösung, verweist den Leser jedoch für weitere Informationen an jenes Rezept. Die beiden folgenden Beispiele enthalten weitere Stylesheets.

Beispiel: Ein wiederverwendbares Stylesheet, das die Vereinigung bezüglich einer Vereinigungsoperation (union) implementiert.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset">
  <xsl:import href="../query/vset.ops.xslt"/>
  <xsl:output method="xml" indent="yes"/>
  <xsl:param name="doc2"/>
  <xsl:template match="/*">
    <xsl:copy>
      <xsl:call-template name="vset:union">
        <xsl:with-param name="nodes1" select="*"/>
        <xsl:with-param name="nodes2" select="document($doc2)/*/*"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Beispiel: Ein Stylesheet, das definiert, welches Element Gleichheit bedeutet.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset">
  <xsl:import href="merge-using-vset-union.xslt"/>
  <xsl:template match="person" mode="vset:element-equality">
    <xsl:param name="other"/>
    <xsl:if test="concat(@lastname,@firstname) = concat($other/@lastname,$other/@firstname)">
      <xsl:value-of select="true()"/>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Die vset:union-basierte Lösung erfordert weniger neuen Code als die schlüsselbasierte Lösung. Bei großen Dokumenten ist jedoch die xsl:key-basierte Lösung wahrscheinlich schneller.

Diskussion

Das Vereinigen von Dokumenten ist oft erforderlich, wenn mehrere Personen oder Prozesse einzelne Teile des Dokuments erzeugen. Auch beim Wiederzusammensetzen eines Dokuments, das für eine Parallelverarbeitung aufgeteilt wurde oder das zu unhandlich war, um es als Ganzes zu behandeln, ist das Vereinigen notwendig.

Die Beispiele in diesem Abschnitt handeln den einfachen Fall ab, bei dem lediglich zwei Dokumente zusammengeführt werden müssen. Wenn eine beliebige Anzahl von Dokumenten vereinigt werden soll, ist ein Mechanismus erforderlich, mit dem eine Liste von Dokumenten an das Stylesheet übergeben werden kann. Eine Technik verwendet einen Parameter, der alle Dateinamen, getrennt durch Leerzeichen, enthält und einen einfachen Tokenizer (Rezept Einen String in Token aufteilen) einsetzt, um die Namen zu extrahieren. Eine andere Technik übergibt alle Dateinamen in das Quelldokument, wie in den beiden folgenden Beispielen gezeigt.

Beispiel: Dokumente, die XML enthalten und vereinigt werden sollen.

<mergeDocs>
  <doc path="people1.xml"/>
  <doc path="people2.xml"/>
  <doc path="people3.xml"/>
  <doc path="people4.xml"/>
</mergeDocs>

Beispiel: Ein Stylesheet zum Vereinigen der Dokumente (vorausgesetzt, dass keine Duplikate im Inhalt vorhanden sind).

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:variable name="docs" select="/*/doc"/>
  <xsl:template match="mergeDocs">
    <xsl:apply-templates select="doc[1]"/>
  </xsl:template>
  <!--Erfassen des ersten Dokuments, um das oberste Element zu erzeugen -->
  <xsl:template match="doc">
    <xsl:variable name="path" select="@path"/>
    <xsl:for-each select="document($path)/*">
      <xsl:copy>
        <!-- Vereinigen der Kinder von Dokument 1 -->
        <xsl:copy-of select="@* | *"/>
        <!--Schleife über die verbleibenden Dokumente, um deren Kinder zu vereinigen -->
        <xsl:for-each select="$docs[position() &gt; 1]">
          <xsl:copy-of select="document(@path)/*/*"/>
        </xsl:for-each>
      </xsl:copy>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de