Das Vereinigen von Dokumenten mit ungleichem Schema

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie haben zwei oder mehrere verschieden strukturierte Dokumente und wollen sie in einem einzigen Dokument vereinigen.

Lösung

Der Vorgang des Vereinigens unähnlicher Daten kann von Anwendung zu Anwendung verschieden ausfallen. Es ist daher nicht möglich, hier nur eine generische Lösung anzubieten. Stattdessen werden gebräuchliche Verfahren herausgestellt, mit denen zwei unterschiedliche Dokumente zusammengebracht werden können, und Lösungen für jeden Fall gezeigt.

Ein Dokument als untergeordneten Teil in ein Elterndokument aufnehmen

Das Aufnehmen eines Dokuments als untergeordneten Teil ist die trivialste Interpretation dieser Art von Vereinigung. Dem liegt die Idee zugrunde, xsl:copy-of einzusetzen, um ein Dokument oder einen Dokumententeil in den entsprechenden Teil eines zweiten Dokuments zu kopieren. Das folgende Beispiel vereinigt zwei Dokumente in einem Container-Dokument, das die Elementnamen im Container dazu verwendet, um anzuzeigen, welche Dateien zusammengeführt werden sollen:

<MyNoteBook>
  <friends></friends>
  <coworkers></coworkers>
  <projects>
    <project>Ersetzen von mapML durch XSLT-Engine mittels Xalan C++</project>
    <project>Sinn des Lebens herausfinden.</project>
    <project>Ermitteln, wo der Wäschetrockner die ganzen fehlenden Socken versteckt</project>
  </projects>
</MyNoteBook>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="friends | coworkers">
    <xsl:copy>
      <xsl:variable name="file" select="concat(local-name( ),'.xml')"/>
      <xsl:copy-of select="document($file)/*/*"/>
    </xsl:copy>
  </xsl:template>
  ...
</xsl:stylesheet>

<?xml version="1.0" encoding="UTF-8"?>
<MyNoteBook>
  <friends>
    <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"/>
  </friends>
  <coworkers>
    <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"/>
  </coworkers>
  <projects>
    <project>Ersetzen von mapML durch XSLT-Engine mittels Xalan C++</project>
    <project>Sinn des Lebens herausfinden.</project>
    <project>Ermitteln, wo der Wäschetrockner die ganzen fehlenden Socken versteckt</project>
  </projects>
</MyNoteBook>

Eine interessante Variante dieses Falls ist ein Dokument, das die inline erfolgende Einbindung eines anderen Dokuments signalisiert. Das W3C definiert dafür eine Standardmethode namens XInclude. Sie können in XSLT einen allgemeinen XInclude-Prozessor implementieren, indem Sie copy.xslt erweitern:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="xi:include" xmlns:xi="http://www.w3.org/2001/XInclude">
    <xsl:for-each select="document(@href)">
      <xsl:apply-templates/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Das xsl:for-each ändert nur den Kontext auf das eingefügte Dokument. Setzen Sie dann xsl:apply-templates ein, um das Kopieren des Inhalts des eingefügten Dokuments fortzusetzen.

Zwei Dokumente zusammenfassen

Eine Variante des einfachen Einfügens kombiniert Elemente, die Kinder gemeinsamer Elternelementtypen sind. Stellen Sie sich zwei Biologen vor, die jeder für sich Informationen über Tiere gesammelt haben. Als ersten Schritt zum Aufbau einer einheitlichen Tierdatenbank könnten sie beschließen, die Daten in Bezug auf strukturelle Gemeinsamkeiten zusammenzufassen.

Biologe 1 hat diese Datei:

<animals>
  <mammals>
    <animal common="Schimpanse" species="Pan troglodytes" order="Primates"/>
    <animal common="Mensch" species="Homo Sapiens" family="Primates"/>
  </mammals>
  <reptiles>
    <animal common="Boa Constrictor" species="Boa constrictor" order="Squamata"/>
    <animal common="Gecko" species="Gekko gecko" order="Squamata"/>
  </reptiles>
  <birds>
    <animal common="Möwe" species="Larus occidentalis" order="Charadriiformes"/>
    <animal common="Schwarzrückenspecht" species="Picoides arcticus" order="Piciformes"/>
  </birds>
</animals>

Biologe 2 hat diese Datei:

<animals>
  <mammals>
    <animal common="Flusspferd" species="Hippopotamus amphibius" family=" Hippopotamidae"/>
    <animal common="Dromedar" species="Camelus dromedarius" family="Camelidae"/>
  </mammals>
  <insects>
    <animal common="Zweipunkt-Marienkäfer" species="Adalia bipunctata" family="Coccinellidae"/>
    <animal common="Mistkäfer" species=" Onthophagus australis" family="Scarabaeidae"/>
  </insects>
  <amphibians>
    <animal common="Grüne Meeresschildkröte" species="Chelonia mydas" family="Cheloniidae"/>
    <animal common="Grüner Laubfrosch" species=" Hyla cinerea" family="Hylidae "/>
  </amphibians>
</animals>

Die Dateien weisen ein ähnliches, aber nicht das gleiche Schema auf. Beide Dateien enthalten die Klasse Mammalia, unterscheiden sich jedoch in den anderen Organisationsebenen. Auf der animal-Ebene hat der eine Biologe Informationen über die Ordnung des jeweiligen Tiers aufgezeichnet, während der andere die Familie des Tiers betrachtet hat. Das folgende Stylesheet fasst die Dokumente auf der Ebene der Klasse des Tiers (d.h. auf der zweiten Ebene in der Dokumentstruktur) zusammen:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*
  <xsl:param name="doc2file"/>
  <xsl:variable name="doc2" select="document($doc2file)"/>
  <xsl:variable name="thisDocsClasses" select="/*/*"/>
  <xsl:template match="/*">
    <xsl:copy>
      <!-- Vereinigen gemeinsamer Abschnitte zwischen Quelldokument und Dokument 2. Enthält auch Abschnitte, die nur im Quelldokument enthalten sind. -->
      <xsl:for-each select="*">
        <xsl:copy>
          <xsl:copy-of select="*"/>
          <xsl:copy-of select="$doc2/*/*[name( ) = name(current( ))]/*"/>
        </xsl:copy>
      </xsl:for-each>
      <!-- Vereinigen von Abschnitten, die nur in Dokument 2 enthalten sind -->
      <xsl:for-each select="$doc2/*/*">
        <xsl:if test="not($thisDocsClasses[name( ) = name(current( ))])">
          <xsl:copy-of select="."/>
        </xsl:if>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Bei der Anwendung des Stylesheets erhält man ein Dokument, das manuell oder durch eine automatisierte Methode weiter normalisiert werden kann:

<animals>
  <mammals>
    <animal common="Schimpanse" species="Pan troglodytes" order="Primates"/>
    <animal common="Mensch" species="Homo Sapiens" order="Primates"/>
    <animal common="Flusspferd" species="Hippopotamus amphibius" family=" Hippopotamidae"/>
    <animal common="Dromedar" species="Camelus dromedarius" family="Camelidae"/>
  </mammals>
  <reptiles>
    <animal common="Boa Constrictor" species="Boa constrictor" order="Squamata"/>
    <animal common="Gecko" species="Gekko gecko" order="Squamata"/>
  </reptiles>
  <birds>
    <animal common="Möwe" species="Larus occidentalis" order="Charadriiformes"/>
    <animal common="Schwarzrückenspecht" species="Picoides arcticus" order="Piciformes"/>
  </birds>
  <insects>
    <animal common="Zweipunkt-Marienkäfer" species="Adalia bipunctata" family="Coccinellidae"/>
    <animal common="Mistkäfer" species=" Onthophagus australis" family="Scarabaeidae"/>
  </insects>
  <amphibians>
    <animal common="Grüne Meeresschildkröte" species="Chelonia mydas" family="Cheloniidae"/>
    <animal common="Grüner Laubfrosch" species=" Hyla cinerea" family="Hylidae "/>
  </amphibians>
</animals>

Elemente aus zwei Dokumenten zusammenführen, um neue Elemente herzustellen

Eine weniger triviale Vereinigung haben wir, wenn zwei Dokumente auf eine Stufe gestellt werden oder wenn aus den Elementen eines Dokuments auf der Grundlage einer bestimmten Charakteristik Kindelemente werden. Betrachten Sie beispielsweise die folgende Vereinigung von Dokumenten, die unterschiedliche Informationen über Leute enthalten:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="copy.xslt"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:param name="doc2file"/>
  <xsl:variable name="doc2" select="document($doc2file)"/>
  <xsl:template match="person">
    <xsl:copy>
      <xsl:for-each select="@*">
        <xsl:element name="{local-name( )}">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:variable name="matching-person" select="$doc2/*/person[@name=concat(current( )/@firstname,' ',current( )/@lastname)]"/>
      <xsl:element name="smoker">
        <xsl:value-of select="$matching-person/@smoker"/>
      </xsl:element>
      <xsl:element name="sex">
        <xsl:value-of select="$matching-person/@sex"/>
      </xsl:element>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Dieses Stylesheet führt zwei Aufgaben aus. Es wandelt attributkodierte Informationen in den eingegebenen Stylesheets in Elemente um und fügt Informationen aus $doc2 hinzu, die im Quelldokument nicht vorhanden sind.

Diskussion

Das Vereinigen von XML mit unterschiedlichem Schema ist weniger gut definiert als das Vereinigen von Dokumenten mit identischem Schema. In diesem Kapitel erörtere ich drei Interpretationen der Vereinigung, es könnten aber noch weitere, kompliziertere Typen existieren. Eine Möglichkeit ist, Dokumente derart zu vereinigen, dass alle drei Arten eine Rolle spielen. Dafür wäre es schwierig, ein generisches, XSLT-basiertes Vereinigungswerkzeug zu erzeugen, das alle speziellen Probleme löst, die bei einer Vereinigung auftreten könnten. Die Beispiele in diesem Abschnitt bieten aber zumindest schon einmal einen guten Einstieg, um ambitioniertere Vereinigungsarten zu realisieren.

Siehe auch

Die Beispiele in diesem Abschnitt konzentrierten sich auf das Vereinigen von Elementen in einer Eins-zu-eins-Beziehung. Das Rezept Joins zeigt Ihnen, wie Sie Informationen aus unterschiedlichem XML aus der Sicht von Datenbankabfragen zusammenführen. Diese Techniken lassen sich dann auch auf das Zusammenführen in einer Eins-zu-viele-Beziehung anwenden.

  

zum Seitenanfang

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