Mit den eingebauten Funktionen für den Export von Daten aus Tabellen und Abfragen in das XML-Format können Sie bereits recht gute Ergebnisse erzielen. Natürlich können Sie aber nicht komplett steuern, wie das Zieldokument später aussehen wird. Je nach den Anforderungen der Anwendung, die das XML-Dokument weiterverarbeiten soll, sind noch änderungen notwendig. Hier tritt die Transformation von XML-Dokumenten auf den Plan: Mit einer sogenannten .xslt-Datei legen Sie fest, wie ein Dokument in ein anderes umgeformt werden soll. Den vollständigen Vorgang steuern Sie dann per VBA-Prozedur. Dieser Beitrag liefert die Grundlagen der Transformation und die notwendigen VBA-Techniken.
Voraussetzungen
Wenn Sie mit den eingebauten Export-Funktionen einfach nur XML-Dokumente auf Basis von Tabellen oder Abfragen erstellen wollen, benötigen Sie dazu keine weiteren Bibliotheken. Auch eine Transformation eines exportierten XML-Dokuments über die entsprechende Funktion der Benutzeroberfläche (zum Beispiel über den Ribbon-Eintrag Externe Daten|Exportieren|XML-Datei) können Sie ohne weitere Hilfsmittel durchführen – Sie können einfach im Assistenten angeben, welche .xslt-Datei die Vorgaben für die Transformation enthält. Das erzeugte XML-Dokument wird dann nach dem Export automatisch auf Basis dieser Datei transformiert. Sollten Sie jedoch einen Export mit der Methode ExportXML des Application-Objekts durchführen wollen, können Sie die .xslt-Datei dort nicht etwa per Parameter angeben. Sie exportieren die Daten dort erst in ein XML-Dokument und führen dann die Transformation durch. Für diese Transformation benötigen Sie Objekte und Methoden der Bibliothek Microsoft XML, vx.0, wobei Sie die jeweils aktuellste Version dieser Bibliothek wählen sollten (s. Bild 1).
Bild 1: Verweis auf die Bibliothek Microsoft XML, v6.0
Transformations-Grundlagen
XML-Dokumente bestehen aus Daten und aus Elementen zur Strukturierung dieser Daten. Damit lassen sich beispielsweise die Daten aus verknüpften Tabellen einer Datenbank hierarchisch darstellen – zum Beispiel haben Sie eine Kategorien-Tabelle und eine Artikel-Tabelle, wo jedem Artikel eine Kategorie zugewiesen ist. In einem XML-Dokument könnten Sie nun die Kategorien und Artikel hierarchisch strukturiert speichern:
<xml version="1.0" encoding="UTF-8"> <Kategorien> <Kategorie> <KategorieID>1</KategorieID> <Kategoriename>Kategorie 1</Kategoriename> <Artikel> <ArtikelID>1</ArtikelID> <Artikelname>Artikel 1</Artikelname> </Artikel> <Artikel> <ArtikelID>2</ArtikelID> <Artikelname>Artikel 2</Artikelname> </Artikel> </Kategorie> <Kategorie> <KategorieID>2</KategorieID> <Kategoriename>Kategorie 2</Kategoriename> <Artikel> <ArtikelID>3</ArtikelID> <Artikelname>Artikel 3</Artikelname> </Artikel> <Artikel> <ArtikelID>4</ArtikelID> <Artikelname>Artikel 4</Artikelname> </Artikel> </Kategorie> </Kategorien>
Die Elemente aus solch einem XML-Dokument können Sie mit einer entsprechenden .xslt-Datei in beliebiger Form umstrukturieren, also transformieren.
Dazu sind nur wenige Schritte nötig:
- Sie benötigen ein Objekt des Typs DOMDocument (oder DOMDocument60, je nach verwendeter Typ-Bibliothek – in unserem Fall Microsoft XML, v6.0 und DOMDocument60), das Sie mit dem Inhalt des zu transformierenden XML-Dokuments füllen.
- Ein weiteres Objekt des gleichen Typs füllen Sie mit dem Inhalt der .xslt-Datei.
- Schließlich brauchen Sie noch ein drittes DOMDocument-Objekt, in welchem die transformierte Datei landet.
- Für das erste DOMDocument-Objekt führen Sie die Methode transformNodeToObject aus, dem Sie das zweite und das dritte DOMDocument-Objekt als Parameter übergeben.
Für diese Anweisungen haben wir eine einfache Prozedur geschrieben, der Sie die Pfade für die drei beteiligten XML-Dokumente per Parameter übergeben können. Diese Prozedur heißt Transformieren und sieht wie in Listing 1 aus. Mit dem ersten Parameter übergeben Sie den Pfad zu der zu transformierenden XML-Datei, mit dem zweiten den Pfad zur .xslt-Datei und mit dem dritten den Pfad, unter dem die transformierte Datei gespeichert werden soll.
Public Function Transformieren(strQuelle As String, strXSLT As String, strZiel As String, _ Optional strFehler As String) As Long Dim objQuelle As MSXML2.DOMDocument60 Dim objXSLT As MSXML2.DOMDocument60 Dim objZiel As MSXML2.DOMDocument60 Set objQuelle = New MSXML2.DOMDocument60 objQuelle.Load strQuelle If objQuelle.parseError = 0 Then Set objXSLT = New MSXML2.DOMDocument60 objXSLT.Load strXSLT If objXSLT.parseError = 0 Then Set objZiel = New MSXML2.DOMDocument60 objQuelle.transformNodeToObject objXSLT, objZiel objZiel.Save strZiel Else Transformieren = objXSLT.parseError.errorCode strFehler = ".xslt-datei: " & vbCrLf & strXSLT & vbCrLf & objXSLT.parseError.reason End If Else Transformieren = objQuelle.parseError.errorCode strFehler = "Quelldatei: " & vbCrLf & strQuelle & vbCrLf & objQuelle.parseError.reason End If End Function
Listing 1: Prozedur für die einfache Transformation eines XML-Dokuments
Der vierte Parameter ist ein optionaler Rückgabeparameter, der von der Funktion mit einer Fehlermeldung gefüllt wird, wenn ein Fehler auftritt. Die Funktion deklariert dann die drei benötigten Objekte vom Typ DOMDocument60.
Dann erstellt sie das erste Objekt objQuelle mit der New-Anweisung und füllt es mit der Load-Methode. Die Load-Methode erwartet den Pfad zu einer XML-Datei, den wir mit dem Parameter strQuelle übergeben. Hierbei kann es geschehen, dass ein Fehler auftritt – beispielsweise, dass unter dem mit strQuelle angegebenen Pfad gar keine Datei gefunden werden kann. Tritt ein solcher Fehler auf, liefert die Eigenschaft parseError von objQuelle einen Wert ungleich 0. Dies prüfen wir in einer If…Then-Bedingung, deren Else-Teil gegebenenfalls die Fehlermeldung in den Rückgabeparameter strFehler schreibt – samt Angabe der fehlerhaften Datei. Außerdem weist die Funktion dem Rückgabewert der Funktion die Fehlernummer zu.
Tritt kein Fehler auf, erstellt die Funktion das zweite Objekt objXSLT und füllt es mit dem Inhalt der mit strXSLT angegebenen .xslt-Datei – wieder unter Verwendung der Load-Methode. Auch hier eventuell auftretende Fehler werden entsprechend behandelt.
Ist bis hierher kein Fehler aufgetreten, erstellt die Prozedur das DOMDocument60-Objekt für das transformierte XML-Dokument. Die Transformation selbst erfolgt dann durch die Methode transformNodeToObject des Objekts objQuelle. Dieser übergeben wir Verweise auf die DOMDocument60-Objekte mit der .xslt-Datei und der Zieldatei als Parameter.
Nach erfolgter Transformation speichern wir den Inhalt des neu erzeugten und gefüllten XML-Dokuments aus objZiel mit der Save-Methode in der mit dem Parameter strZiel angegebenen XML-Datei.
Aufruf der Funktion “Transformieren”
Der Aufruf dieser Funktion kann, wenn Sie das zu transformierende XML-Dokument und das .xslt-Dokument bereits auf der Festplatte abgelegt haben, ganz einfach wie folgt geschehen:
Transformieren <Quelldokument>, <XSLT-Dokument>, <Zieldokument>
So erhalten Sie zwar keinen Zugriff auf eine eventuelle Fehlermeldung, aber es ist der schnellste Weg, um die Transformation durchzuführen, wenn die Dateien im Dateisystem liegen.
Wenn Sie sich die Funktionsweise inklusive Fehlermeldung ansehen möchten, können Sie die Methode aus Listing 2 nutzen – gemeinsam mit den Tabellen der Beispieldatenbank zu diesem Beitrag. Die Methode deklariert zunächst die benötigten Variablen. Dann exportiert sie ein XML-Dokument auf Basis der Tabellen tblKategorien und tblArtikel, wobei die Artikeldaten den Kategorie-Elementen untergeordnet werden sollen (wie dies im Detail funktioniert, lesen Sie im Beitrag XML-Export mit VBA, www.access-im-unternehmen.de/1046).
Public Sub TestTransformieren() Dim strQuelle As String Dim strXSLT As String Dim strZiel As String Dim strFehler As String Dim lngFehler As Long Dim objAdditionalData As AdditionalData Set objAdditionalData = Application.CreateAdditionalData objAdditionalData.Add "tblArtikel" strQuelle = CurrentProject.Path & "\KategorienUndArtikel_Untransformiert.xml" Application.ExportXML acExportTable, "tblKategorien", strQuelle, AdditionalData:=objAdditionalData strXSLT = CurrentProject.Path & "\KategorienUndArtikel.xslt" strZiel = CurrentProject.Path & "\KategorienUndArtikel_Transformiert.xml" lngFehler = Transformieren(strQuelle, strXSLT, strZiel, strFehler) If Not lngFehler = 0 Then MsgBox strFehler End If End Sub
Listing 2: Aufruf der Funktion Transformieren mit Beispieldaten
Der Export landet in der Datei KategorienUndArtikel_Untransformiert.xml. Die .xslt-Datei zu diesem Beispiel finden Sie in den Download-Dateien. Sie heißt KategorienUndArtikel.xslt und sollte sich im gleichen Verzeichnis wie die Datenbank befinden. Schließlich legt die Prozedur noch den Namen der Zieldatei fest, die unter KategorienUndArtikel_Transformiert.xml gespeichert werden soll. Die drei Variablen strQuelle, strXSLT und strZiel werden samt der Variablen strFehler für den optionalen Parameter an die Funktion Transformieren übergeben. Sollte hier einer der oben erläuterten Fehler auftreten, liefert diese einen Wert ungleich 0 zurück, was zur Ausgabe der mit strFehler zurückgegebenen Fehlermeldung per MsgBox-Anweisung führt. Anderenfalls finden Sie nun in der Datei KategorienUndArtikel_Transformiert.xml das transformierte XML-Dokument vor. Sie können sich das Beispiel vorab anhand der Beispieldaten anschauen, in den folgenden Abschnitten erläutern wir die einzelnen Elemente einer .xslt-Datei.
XSLT
XSLT ist die Sprache, mit der Transformationen von XML-Dateien in andere XML-Dateien oder auch HTML-Dateien durchgeführt werden. Dabei greifen Sie über eine spezielle weitere Sprache namens XPath auf das oder die gewünschten Elemente des zu transformierenden XML-Dokuments zu und überführen die kompletten Elemente oder auch nur deren Inhalt in das zu erstellende Dokument. Die Sprache XPath und ihre Anwendung mittels VBA beschreiben wir in einem weiteren Beitrag namens VBA und XPath (www.access-im-unternehmen.de/1050).
Wenn Sie schon einmal eine Webseite programmiert haben, die nicht nur aus reinem HTML besteht, sondern auch aus Skript-Elementen etwa auf Basis von PHP oder ASP/ASP.NET, haben Sie unbewusst bereits eine Vorstellung davon, wie XSLT ein neues Dokument auf Basis eines bestehenden Dokuments zusammensetzt. Ein XSLT-Dokument ist dabei ähnlich aufgebaut wie eine aus Skript- und HTML-Teilen bestehende Webseite.
Sie finden dort nämlich feste Zeichenketten, aber auch dynamische Elemente, mit denen etwa die Inhalte des zu transformierenden Dokuments ermittelt und ausgegeben werden.
XSLT deklarieren
Damit die .xslt-Datei korrekt interpretiert werden kann, teilen wir der jeweiligen Verarbeitungsinstanz (in unserem Beispiel etwa die Methode transformNodeToObject) mit einer entsprechenden Deklaration in der ersten Zeile mit, um was für einen Dokumenttyp es sich handelt. In diesem Fall soll die Datei mit der folgenden Zeile starten:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/TR/REC-html40">
Damit wird der offizielle Namespace des W3C-Konsortiums vorgegeben. Alle folgenden Zeilen, die XSLT-Befehle enthalten, starten mit <xsl: und werden mit einem XSLT-Schlüsselwort fortgesetzt. Dadurch können Sie die auszuführenden Elemente des .xslt-Dokuments von den statischen Elementen unterscheiden – ähnlich wie etwa beim einem PHP-Dokument, wo die PHP-Anweisungen in
Dieser Zeile stellen wir noch die folgende Zeile voran:
<xml version="1.0" encoding="UTF-8">
Das template-Element
Das Basis-Element einer .xslt-Datei ist das template-Element. Es enthält auch ein Attribut namens match. Mit match referenzieren Sie das Element eines XML-Dokuments, auf das sich die innerhalb des template-Elements befindlichen Elemente beziehen. Der Wert von match ist ein XPath-Ausdruck. XPath ist, wie oben bereits erwähnt wurde, die Sprache für den Zugriff auf die Elemente in einem XML-Dokument. Jede Menge Beispiele dazu finden Sie im Beitrag VBA und XPath (www.access-im-unternehmen.de/1050). Wenn Sie beispielsweise auf das Root-Element des Dokuments (also das oberste Element) zugreifen wollen, geben Sie für das Attribut match einen Schrägstrich an (/).
Es werden nur Informationen ausgegeben, die sich innerhalb eines template-Elements befinden. Sie können also Folgendes in die .xslt-Datei schreiben und es wird nichts ausgegeben:
<xml version="1.0" encoding="utf-8"> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/TR/REC-html40"> <blabla>blub</blabla> <xsl:template match="/"> </xsl:template> </xsl:stylesheet>
Innerhalb des template-Elements befinden sich keine Daten, und das davor angegebene blabla-Element wird nicht ausgegeben, weil es sich nicht innerhalb eines template-Elements befindet. Das Ergebnisdokument ist folglich leer. Wenn Sie das blabla-Element innerhalb des template-Elements platzieren, wird es allerdings ausgegeben:
<xsl:stylesheet version="1.0" ...> <xsl:template match="/"> <blabla>blub</blabla> </xsl:template> </xsl:stylesheet>
Das heißt, dass Sie selbst eigene Elemente zur Ausgabe hinzufügen können, auch ohne dynamische xsl:…-Elemente innerhalb des template-Elements hinzuzufügen. Sie könnten also etwa die Grundstruktur des Dokuments anlegen:
<xsl:stylesheet version="1.0" ...> <xsl:template match="/"> <Bestellverwaltung> </Bestellverwaltung> </xsl:template> </xsl:stylesheet>
Dies liefert die folgende Ausgabe:
<xml version="1.0" encoding="UTF-16"> <Bestellverwaltung ...></Bestellverwaltung>
Zeilenumbruch herstellen
XML-Dokumente haben den Vorteil, dass sie sowohl maschinell erfasst werden können also auch durch das menschliche Auge in den meisten Fällen gut verarbeitet werden können. Dies fällt jedoch umso leichter, wenn der Inhalt des Dokuments einigermaßen strukturiert ausgegeben wird – also mit Zeilenumbrüchen und Einrückungen. Das vorherige Beispiel enthält keine Zeilenumbrüche, was bei dem Hauptelement
<xsl:stylesheet version="1.0" ...> <xsl:template match="/"> <Bestellverwaltung> <xsl:text> </xsl:text> </Bestellverwaltung> </xsl:template> </xsl:stylesheet>
Damit erhalten wir nun im Zieldokument:
<xml version="1.0" encoding="UTF-16"> <Bestellverwaltung xmlns="http://www.w3.org/TR/REC-html40"> </Bestellverwaltung>
Das ist viel besser – darauf können wir aufbauen! Das Element
Kommentare
Wenn Sie während der Erstellung eines .xslt-Dokuments Bereiche auskommentieren wollen, finden Sie dazu ein eigenes Element. Dieses heißt comment und wird beispielsweise wie folgt eingesetzt:
<xsl:comment> ... auszukommentierender Bereich </xsl:comment>
Daten aus dem Originaldokument ausgeben
Nun wollen wir endlich auf die Daten in unserem Ausgangsdokument zugreifen, das wir transformieren wollen. Bereits jetzt wird offensichtlich, dass es eher eine Neuerstellung eines Dokuments ist als eine Transformation, denn wir müssen wohl für jedes einzelne gewünschte Element festlegen, ob und wo wir es platzieren wollen.
Wenn Sie nur den Inhalt eines bestimmten Elements des Ausgangsdokuments ausgeben wollen, verwenden Sie dazu das value-of-Element. Dieses erwartet mit dem select-Attribut die Angabe des betroffenen Elements, das Sie wiederum mit einem XPath-Ausdruck definieren.
Wir möchten einfach den Namen der ersten Kategorie in unserem Ausgangsdokument ermitteln. Damit wir wissen, von welchem Aufbau wir beim Auslesen des Dokuments reden, haben wir dieses auszugsweise in Listing 3 abgebildet. Dieses Dokument wird mit der Prozedur TestTransformieren aus den beiden Tabellen tblKategorien und tblArtikel der Beispieldatenbank erzeugt, die wir weiter oben vorgestellt haben.
<xml version="1.0" encoding="UTF-8"> <dataroot xmlns:od="urn:schemas-microsoft-com:officedata" generated="2016-07-09T10:30:28"> <tblKategorien> <KategorieID>1</KategorieID> <Kategoriename>Getränke</Kategoriename> <Beschreibung>Alkoholfreie Getränke, Kaffee, Tee, Bier</Beschreibung> <Abbildung>...</Abbildung> <tblArtikel> <ArtikelID>1</ArtikelID> <Artikelname>Chai</Artikelname> <LieferantID>1</LieferantID> <KategorieID>1</KategorieID> <Liefereinheit>10 Kartons x 20 Beutel</Liefereinheit> <Einzelpreis>9</Einzelpreis> <Lagerbestand>39</Lagerbestand> <BestellteEinheiten>0</BestellteEinheiten> <Mindestbestand>10</Mindestbestand> <Auslaufartikel>0</Auslaufartikel> </tblArtikel> <tblArtikel> <ArtikelID>2</ArtikelID> <Artikelname>Chang</Artikelname> ... </tblArtikel> ... </tblKategorien> <tblKategorien> <KategorieID>2</KategorieID> <Kategoriename>Gewürze</Kategoriename> <Beschreibung>Süße und saure Soßen, Gewürze</Beschreibung> <Abbildung>...</Abbildung> <tblArtikel> <ArtikelID>3</ArtikelID> <Artikelname>Aniseed Syrup</Artikelname> ... </tblArtikel> ... </tblKategorien> ... </dataroot>
Listing 3: Ausgangsdokument für unsere Experimente
Wir möchten also nun auf den Inhalt des Elements Kategoriename unterhalb von dataroot und tblKategorien zugreifen. Dazu fügen wir unserem Dokument nun einfach eine Zeile mit dem value-of-Element und der Angabe des gesuchten Elements, also dataroot/tblKategorien/Kategoriename hinzu:
... <Bestellverwaltung> <xsl:text> </xsl:text> <xsl:value-of select="dataroot/tblKategorien/Kategoriename"/> <xsl:text> </xsl:text> </Bestellverwaltung> ...
Dies liefert nun ein XML-Dokument mit folgendem Inhalt:
<xml version="1.0" encoding="UTF-16"> <Bestellverwaltung xmlns="http://www.w3.org/TR/REC-html40"> Getränke </Bestellverwaltung>
Oh – das ist zwar ein gültiges XML-Dokument, aber wir wollen die Kategorie natürlich in ein eigenes Element stecken.
Dazu fügen wir einfach ein paar statische Elemente zum .xslt-Dokument hinzu, sodass wir sowohl ein Kategorie– als auch ein Kategoriename-Element erhalten:
... <xsl:template match="/"> <Bestellverwaltung> <xsl:text> </xsl:text> <Kategorie> <xsl:text> </xsl:text> <Kategoriename> <xsl:value-of select="dataroot/tblKategorien/Kategoriename"/> </Kategoriename> <xsl:text> </xsl:text> </Kategorie> <xsl:text> </xsl:text> </Bestellverwaltung> </xsl:template> ...
Innerhalb der .xslt-Datei haben wir überall dort, wo Zeilenumbrüche eingefügt werden sollen, das Element
Das Ergebnis sieht schon eher nach XML aus:
<xml version="1.0" encoding="UTF-16"> <Bestellverwaltung xmlns="http://www.w3.org/TR/REC-html40"> <Kategorie> <Kategoriename>Getränke</Kategoriename> </Kategorie> </Bestellverwaltung>
Attributwerte transformieren
Wenn das Ausgangsdokument Elemente mit Attributen enthält, auf die Sie per XSLT zugreifen wollen, gelingt dies ebenfalls mit dem value-of-Element. Sie müssen hier nur den entsprechenden select-Ausdruck für ein Attribut verwenden. Auf die ArtikelID in
<xsl:value-of select="Artikel/@ArtikelID">
Elemente per Schleife durchlaufen
Allerdings haben wir nun erst eine einzige Kategorie in das Zieldokument übertragen. Wie gelangen wir an die übrigen
Sie ahnen es bereits: Genau, wie es ein Element gibt, mit dem man auf das Ausgangselement zeigen kann (
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo