Wir haben uns bereits in vielen Beiträgen mit dem Parsen von XML-Dokumenten befasst. Dabei haben wir immer den DOM-Parser verwendet, um die im XML-Dokument enthaltenen Daten zu lesen. In diesem Beitrag schauen wir uns eine Alternative namens SAX an. Während DOM das komplette XML-Dokument in den Speicher liest und darauf zugreift, liest SAX das Dokument zeilenweise ein. Im Folgenden erfahren Sie, welche Vor- und Nachteile die beiden Methoden haben und wie Sie mit SAX arbeiten können.
Der DOM-Parser liest ein vollständiges XML-Dokument in den Speicher ein und kann dafür auf alle enthaltenen Elemente zugreifen. Sie können die Elemente damit nacheinander durchlaufen oder mithilfe von XPATH-Ausdrücken und Methoden wie SelectSingleNode oder SelectNodes nach bestimmten Elementen im Dokumentbaum suchen.
Sie können damit im Dokumentbaum vor- und zurückspringen oder die enthaltenen Elemente hierarchisch durchlaufen.
DOM: Vor- und Nachteile
Der Nachteil liegt auf der Hand: Da der komplette Baum im Speicher liegt und mitunter verschachtelt ist, kann der Zugriff auf die Inhalte recht schnell zu Lasten der Performance gehen.
Ein weiterer Nachteil ist, dass sehr große XML-Dateien möglicherweise nicht in den Speicher passen und somit nicht geladen werden können. Dafür können Sie mit DOM aber nicht nur gezielt auf einzelne Elemente zugreifen, sondern den Dokumentbaum und die Inhalte auch bearbeiten.
SAX: Schnell, aber unflexibel
Anders sieht es bei SAX aus. Der SAX-Parser liest das Dokument Zeile für Zeile ein und bietet die Möglichkeit, auf die jeweilige Zeile zuzugreifen. Dabei ist der SAX-Parser rasend schnell! Allerdings liest er auch nur in einer Richtung, ein Hin- und Herspringen oder gar das gezielte Einlesen eines einzelnen Elements ohne Durchlaufen des XML-Dokuments bis zu diesem Punkt ist nicht möglich.
Außerdem können Sie mit dem SAX-Parser keine änderungen am XML-Dokument vornehmen.
Vorbereitungen
Um den SAX-Parser nutzen zu können, benötigen Sie einen Verweis auf die Objektbibliothek Microsoft XML v6.0 (s. Bild 1).
Bild 1: Setzen eines Verweises auf die Bibliothek Microsoft XML, v6.0
Funktionsweise des SAX-Parsers
Wie aber greift der SAX-Parser nun auf das XML-Dokument zu öffnet er es wie ein Textdokument und liest dann etwa in einer Schleife die einzelnen Zeilen in eine Variable ein, auf deren Inhalt wir dann per VBA zugreifen können
Mitnichten: Der SAX-Parser basiert auf Ereignissen. Das heißt, dass es verschiedene Ereignisse gibt:
- documentLocator(ByVal RHS As MSXML2.IVBSAXLocator): Liefert Informationen etwa über den Speicherort des Dokuments.
- startDocument(): Wird zu Beginn des Einlesevorgangs ausgelöst.
- startPrefixMapping(strPrefix As String, strURI As String): Wird beim Einlesen der Namespace-Informationen ausgelöst und liefert mit strPrefix das Kürzel für den NameSpace (zum Beispiel xsi oder xsd) und mit strURI die jeweilige URI.
- startElement(strNamespaceURI As String, strLocalName As String, strQName As String, ByVal oAttributes As MSXML2.IVBSAXAttributes): Wird für den Start eines jeden Elements ausgelöst (also etwa für
) und liefert mit strNameSpaceURI den Namespace, mit strLocalName den Namen des Elements und mit oAttributes ein Element, mit dem sich die Attribute des Elements auslesen lassen. - characters(strChars As String): Wird beim Einlesen eines Teils des Inhalts ausgelöst und liefert mit strChars den Inhalt. Dabei werden immer alle Zeichen geliefert, also auch die Zeilenumbrüche zwischen zwei Elementen, die Einrückungen und natürlich auch die tatsächlichen Inhalte der Elemente.
- endElement(strName-spaceURI As String, strLocalName As String, strQName As String): endElement wird ausgelöst, wenn das Ende des Elements erreicht wird, also etwa . Die Parameter liefern die bereits weiter oben beschriebenen Werte.
- endPrefix-Map-ping(str-Prefix As String): Wird ausgelöst, wenn das schließende Element zu dem Element erreicht wird, das eine Namespace-Angabe enthielt.
- endDocument(): Wird nach der Abarbeitung des letzten Elements ausgelöst.
Die übrigen Ereignisse benötigen wir für die Beispiele dieses Beitrags nicht.
Ereignisse implementieren
Wie implementieren wir diese Ereignisse nun Als Erstes erstellen Sie ein neues Klassenmodul namens clsSAX. Normalerweise würde man diesem nun eine entsprechende Objektvariable hinzufügen, die mit dem Schlüsselwort WithEvents deklariert wird und so die enthaltenen Ereignisprozeduren verfügbar macht.
In diesem Fall ist dies anders: Wir deklarieren keine Objektvariable, sondern legen mit der Implements-Anweisung fest, dass die Klasse alle Ereignisprozeduren eines speziellen Interfaces implementiert – in diesem Falle für das Interface MSXML2.IVBSAXContentHandler (s. Bild 2).
Bild 2: Implementieren der Klasse IVBSAXContentHandler
Dessen Ereignisse können Sie dann jedoch genau wie bei mit WithEvents deklarierten Objekten mit den beiden Kombinationsfeldern im oberen Bereich des Codefensters festlegen. Als Erstes fügen wir so das Ereignis startDocument hinzu (s. Bild 3).
Bild 3: Implementieren eines Ereignisses der Klasse IVBSAXContentHandler
Im Gegensatz zu mit Objekten, die Ereignisse auslösen, müssen Sie jedoch bei der Implementierung eines Interfaces alle Elemente anlegen. Anderenfalls löst das Debuggen des VBA-Projekts den Fehler aus Bild 4 aus. Wenn Sie alle Ereignisprozeduren hinzugefügt haben, ist der erste Teil erledigt.
Bild 4: Die Implementierung der Klasse IVBSAXContentHandler ist noch nicht vollständig.
Nun liegt uns allerdings zunächst einmal eine Klasse vor, die allein noch nichts ausrichtet. Wir müssen zunächst ein Objekt auf Basis dieser Klasse deklarieren und instanzieren sowie der Klasse mitteilen, dass sie in Aktion treten soll.
Dies erledigen wir gleich einmal in einem Formular, das auch die Steuerelemente zum Auswählen der zu untersuchenden XML-Datei sowie eine Schaltfläche zum Starten des Vorgangs enthalten soll. Dieses Formular sieht im ersten Entwurf wie in Bild 5 aus. Die Schaltfläche Einlesen soll das angegebene XML-Dokument mithilfe der Klasse clsSAX einlesen.
Bild 5: Das Formular frmSAX
Dazu legen Sie die Ereignisprozedur aus Listing 1 an. Die Prozedur deklariert gleich zwei Objektvariablen: objSax soll ein Objekt auf Basis der soeben erstellten Klasse clsSAX aufnehmen. objReader nimmt ein Objekt des Typs SAXXMLReader60 auf, das Teil der Objektbibliothek Microsoft XML, v6.0 ist.
Private Sub cmdEinlesen_Click() Dim objReader As SAXXMLReader60 Dim objSax As clsSAX Set objReader = New SAXXMLReader60 Set objSax = New clsSAX Set objReader.contentHandler = objSax objReader.parseURL Me!txtDatei Set objReader = Nothing End Sub
Listing 1: Diese Prozedur startet den Einlesevorgang.
Nun weisen Sie der Eigenschaft contentHandler der Klasse objReader den Verweis auf das Objekt objSax zu und rufen dann die Methode parseURL der Klasse objReader mit der einzulesenden Datei als Parameter auf. Das war der Rahmen – mit einem Klick auf die Schaltfläche cmdEinlesen starten Sie nun den Einlesevorgang, der die Ereignisse der Klasse clsSAX auslöst.
Dabei geschieht aber offensichtlich noch gar nichts – was aber auch kein Wunder ist: Immerhin haben wir den Ereignisprozeduren ja auch noch keine einzige Zeile Code hinzugefügt.
Einlesevorgang programmieren
Dies gehen wir im folgenden Schritt an. Wir wollen zunächst einmal erfahren, welche Informationen der Parser zu den einzelnen Elementen liefert – und zwar am Beispiel einer einfachen XML-Datei wie der aus Listing 2.
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