XML-Dokumente transformieren mit XSL

Im Artikel Kategorie-XML-Export mit Transformation haben wir an einem Praxisbeispiel gezeigt, wie Sie die Daten aus den beiden Tabellen Kategorien und Artikel exportieren und in eine andere Form bringen, als es der reine Export getan hätte. Im vorliegenden Artikel schauen wir uns nun an, welche Möglichkeiten die Transformation mit XSL grundsätzlich bietet und welche Rolle dabei die Sprache XPath spielt.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1903_XMLTransformieren.zip.

Eine tragende Rolle für die Beispiele der folgenden Seiten spielt die Funktion Transformieren aus Listing 1. Diese Funktion erwartet drei per Parameter übergebene Werte: Den Pfad zur Quelldatei, den Pfad zur XSL-Datei und den Pfad der zu erstellenden Datei. Der vierte Parameter ist optional und liefert einen Text zu einem eventuell beim Parsen der beteiligten XML-Dokumente gefundenen Fehler zurück.

Public Function Transformieren(strQuelle As String, strXSL As String, strZiel As String, Optional strFehler As String) As Long
      Dim objQuelle As MSXML2.DOMDocument
      Dim objXSL As MSXML2.DOMDocument
      Dim objZiel As MSXML2.DOMDocument
      Set objQuelle = New MSXML2.DOMDocument
      objQuelle.Load strQuelle
      If objQuelle.parseError = 0 Then
          Set objXSL = New MSXML2.DOMDocument
          objXSL.Load strXSL
          If objXSL.parseError = 0 Then
              Set objZiel = New MSXML2.DOMDocument
              objQuelle.transformNodeToObject objXSL, objZiel
              objZiel.Save strZiel
          Else
              Transformieren = objXSL.parseError.errorCode
              strFehler = ".xsl-datei: " & vbCrLf & strXSL & vbCrLf & objXSL.parseError.reason
          End If
      Else
          Transformieren = objQuelle.parseError.errorCode
          strFehler = "Quelldatei: " & vbCrLf & strQuelle & vbCrLf & objQuelle.parseError.reason
      End If
End Function

Listing 1: Die Prozedur Transformieren

Mit dieser Funktion werden wir die folgenden Beispiele dieses Artikels durchführen. Sie lädt als erstes das XML-Dokument aus strQuelle mit der Load-Methode in das DOMDocument-Element objQuelle. Dann prüft sie, ob das Dokument einen Fehler enthält. Falls nicht, lädt sie das XSL-Dokument in die Variable objXSL.

Auch hier folgt eine Prüfung auf eventuelle Fehler beim Parsen der Datei. Erfolgt auch dies fehlerfrei, ruft die Prozedur die transformNodeToObject-Methode von objQuelle auf und übergibt objXSL als ersten und objZiel als zweiten Parameter. Danach speichert sie das neu erstellte Objekt mit der Save-Methode des Objekts objZiel, und zwar unter dem Namen, der in der Variablen strZiel angegeben ist.

Sollte an irgendeiner Stelle ein Fehler beim Parsen aufgetreten sein, liefert

Für all dies ist ein Verweis auf die Bibliothek Microsoft XML, v3.0 erforderlich. Es gibt zwar moderne Versionen dieser Bibliothek, aber wir verwenden diese, weil die XPath-Befehle damit besser funktionieren – mehr dazu im Artikel XML-Dokumente mit XPath durchsuchen.

Diesen Verweis fügen wir über den Verweise-Dialog hinzu, den Sie im VBA-Editor mit dem Menüeintrag Extras|Verweise aktivieren (siehe Bild 1).

Hinzufügen eines Verweises auf die XML-Bibliothek

Bild 1: Hinzufügen eines Verweises auf die XML-Bibliothek

Wir gehen an dieser Stelle davon aus, dass Sie bereits eine Beispiel-XML-Datei erstellt haben, die im gleichen Verzeichnis wie die Datenbank liegt.

Fehler ausgeben

Um eventuell auftretende Fehler auszugeben, rufen wir die Prozedur Transformieren wie folgt auf und übergeben dabei noch die Variable strFehler:

Public Sub TransformierenMitMeldungen()
    Dim strFehler As String
    Transformieren CurrentProject.Path                         & "\KategorienUndArtikel.xml",                                    CurrentProject.Path                         & "\KategorienUndArtikel.xsl",            CurrentProject.Path & "\Transformiert.xml",                                                strFehler
    Debug.Print strFehler
End Sub

Tritt hier ein Fehler auf, wird dieser mit dem Parameter strFehler an die aufrufende Prozedur zurückgegeben. Diese gibt ihn dann im Direktbereich des VBA-Editors aus.

Beispieldokument als Quelle

Als Beispiel verwenden wir wie in einigen anderen Artikeln den Export der Tabelle tblKategorien, für die wir zusätzlich die Tabelle tblArtikel hinzugefügt haben. Diese sieht in stark gekürzter Form wie folgt aus:

<xml version="1.0" encoding="UTF-8">
<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" generated="2019-08-01T21:45:19">
<tblKategorien>
<KategorieID>1</KategorieID>
<Kategoriename>Getränke</Kategoriename>
<Beschreibung>Alkoholfreie Getränke...</Beschreibung>
<Abbildung>FRwvAAIAAAANAA4AFAAhAP...</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>
...
</tblKategorien>
...
</dataroot>

Im einführenden Beispiel des Artikels Kategorie-XML-Export mit Transformation haben wir aus diesem Dokument ein Dokument gemacht, das so aussieht, wie wir uns ein solches Dokument vorgestellt hätten, also etwa so:

<xml version="1.0" encoding="UTF-16">
<Bestellverwaltung xmlns="http://www.w3.org/TR/REC-html40">
  <Kategorien>
    <Kategorie ID="1">
      <Kategoriename>Getränke</Kategoriename>
      <Beschreibung>Alkoholfreie Getränke, Kaffee,                                 Tee, Bier</Beschreibung>
      <Artikelliste>
        <Artikel ID="1">
          <Artikelname>Chai</Artikelname>
        </Artikel>
        ...
      </Artikelliste>
    </Kategorie>
    ...
  </Kategorien>
</Bestellverwaltung>

Für die komplette XSL-Datei, die wir hier verwendet haben, verweisen wir Sie auf den Artikel Kategorie-XML-Export mit Transformation.

Kopf einer XSL-Datei

Die erste Zeile einer XSL-Datei sieht immer wie folgt aus:

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns="http://www.w3.org/TR/REC-html40">

Dynamische und statische Anweisungen

Danach folgen einige weitere Anweisungen, die sich vor allem durch ein Merkmal unterscheiden: Sie beginnen entweder mit <xsl: oder eben nicht. Wenn Sie schon einmal eine PHP- oder ASP-Seite programmiert haben, kennen Sie das Prinzip: Sie mischen dort HTML-Code und dynamische Skriptbestandteile. Der HTML-Code wird 1:1 in das resultierende HTML-Dokument übernommen, der PHP- oder ASP-Code fügt weitere, dynamisch ermittelte Inhalte etwa aus einer Datenbank hinzu oder strukturiert die Inhalte etwa durch den Einsatz von Schleifen oder Bedingungen. Genau das Gleiche finden Sie auch in XSL-Dokumenten vor. Der Unterschied ist, dass die Daten, die im Zieldokument abgebildet werden sollen, aus dem Quelldokument stammen und nicht etwa aus einer Datenbank.

Das template-Element als Wurzelelement

Genau wie ein XML-Dokument hat auch das XSL-Dokument ein Wurzelelement, nämlich das Element template. Dieses verlangt zwingend nach der Angabe des Parameters match. match gibt an, auf welches Element der Originaldatei sich die nachfolgenden Anweisungen beziehen beziehungsweise welches Element samt Unterelementen die Daten für das Zielelement liefern soll. Hier stellen wir match mit „/“ auf das Wurzelelement ein. Außerdem wollen wir als Basis ein öffnendes und schließendes Element namens Bestellverwaltung anlegen:

<xsl:stylesheet version="1.0"      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="/">
     <Bestellverwaltung>
     </Bestellverwaltung>
   </xsl:template>
</xsl:stylesheet>

Das Ergebnis sieht dann so aus:

<xml version="1.0" encoding="UTF-16">
<Bestellverwaltung></Bestellverwaltung>

Zeilenumbrüche

Hier wollen wir nun zunächst einen Zeilenumbruch einfügen, denn obwohl wir das öffnende und schließende Bestellverwaltung-Element in zwei Zeilen geschrieben haben, wird der Zeilenumbruch nicht in das Ergebnis übernommen. Das gelingt nur, wenn das entsprechende Zeichen innerhalb eines öffnenden und schließenden Elements xsl:text eingefasst wird, also etwa so:

<Bestellverwaltung>
 <xsl:text>
 </xsl:text>
 </Bestellverwaltung>

Einzelne Elemente des Quelldokuments ausgeben

Sie können auf einzelne Elemente des Quelldocuments mit dem Element xsl:value-of zugreifen und dabei den Pfad zu dem Element mit dem Attribut select angeben.

Wenn sie etwa den Wert des ersten KategorieID-Elements unterhalb der Elemente dataroot/tblKategorien ausgeben wollen, verwenden Sie:

<xsl:value-of select="/dataroot/tblKategorien/KategorieID"/>

Für das Feld Kategoriename nutzen Sie:

<xsl:value-of select="/dataroot/tblKategorien/Kategoriename"/>

Sie müssen also den kompletten Pfad vom Wurzelelement aus angeben.

<xsl:value-of select="/dataroot/tblKategorien/KategorieID"/>

Wenn es Elemente des Typs KategorieID nur an einer Stelle gibt, können Sie auch gezielt auf das erste überhaupt verfügbare Element KategorieID zugreifen:

<xsl:value-of select="//KategorieID"/>

In diesem Fall bedeutet //, dass an beliebiger Stelle unabhängig von der Ebene gesucht wird.

Leerzeichen zwischen Elemente

übrigens können Sie auch Leerzeichen zwischen zwei zusammengesetzte Textelemente nur mit xsl:text einfügen. Einfach ein Leerzeichen wie hier reicht nicht:

<xsl:value-of select="//KategorieID"/> 

Stattdessen fügen Sie das Leerzeichen in xsl:text eingefasst hinzu:

<xsl:value-of select="//KategorieID"/><xsl:text> </xsl:text><xsl:value-of select="//Kategoriename"/>

Schleifen mit for-each

Mit xsl:value-of greifen Sie nur auf den ersten gefundenen Wert zu. Wenn Sie alle Werte mit diesem Pfad zugreifen wollen, etwa in einer Schleife, können Sie xsl:for-each verwenden.

Möchten Sie weiterlesen? Dann lösen Sie Ihr Ticket!
Hier geht es zur Bestellung des Jahresabonnements des Magazins Access [basics]:
Zur Bestellung ...
Danach greifen Sie sofort auf alle rund 400 Artikel unseres Angebots zu - auch auf diesen hier!
Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar