Den Parser arbeiten lassen

(Auszug aus "Perl & XML" von Erik T. Ray & Jason McIntosh)

Genug der internen Parserdetails! Wir wollen endlich sehen, was man mit der Ausgabe des Parsers tatsächlich anfangen kann. Ein vollständiges Beispiel einer durch den Parser erzeugten Baumstruktur kennen wir bereits aus dem Beispiel Aufbau eines XML-Baums, also wollen wir nun den anderen Typ etwas näher betrachten. Wir klinken uns in einen XML-Eventstrom ein und lassen eigenen Code mit den Events arbeiten. Was dabei herauskommt, ist wirklich nicht besonders nützlich, aber es zeigt sehr schön, wie echte XML-Prozessoren aussehen können.

An der Eingabeseite unseres Programms sitzt XML::Parser (intern also Expat). Expat ist ein eventorientierter Parser, wie wir sie gerade beschrieben haben. Statt das gesamte XML-Dokument in den Speicher zu laden, soll der Parser immer dann anhalten, wenn er den Beginn bzw. das Ende eines Tags findet. Das Programm prüft, ob es irgend etwas mehr oder weniger Sinnvolles mit den übergebenen Daten anfangen kann, und läßt den Parser dann weiterarbeiten.

Ihre wichtigste Aufgabe ist die Definition einer Schnittstelle zu den Teilen Ihres Programms, die die verschiedenen Events bearbeiten. Jeder Eventtyp kann einer eigenen Subroutine zugeordnet werden, die wir als Handler bezeichnen. Wir registrieren unsere Handler, indem wir bei der Initialisierung des Parsers entsprechende Optionen setzen. Das folgende Beispiel zeigt den gesamten Vorgang.

Beispiel: Ein eventorientierter XML-Prozessor

use XML::Parser;

# Initialisierung des Parsers
my $parser = XML::Parser->new( Handlers =>
                              {
                              Start=>\&handle_start,
                              End=>\&handle_end,
                              });
$parser->parsefile( shift @ARGV );

my @element_stack; # Speicherung der momentan geöffneten Elemente

# Verarbeitung des Events "Start eines Elements": Gib eine Nachricht aus
#
sub handle_start {      
      my( $expat, $element, %attrs ) = @_;
        
      # Das Expat-Objekt wird nach der aktuellen Zeilennummer gefragt
      my $line = $expat->current_line;
        
      print "Ein Element $element beginnt in Zeile $line!\n";
        
      # Speichere das Element und seine Startposition in Form einer Hashreferenz
      # auf dem Stack.
      push( @element_stack, { element=>$element, line=>$line });
        
      if( %attrs ) {
      print "Das Element besitzt die folgenden Attribute:\n";
      while( my( $key, $value ) = each( %attrs )) {
      print "\t$key => $value\n";
    }
  }
}

# Verarbeitung des Events "Ende eines Elements": Gib eine Nachricht aus.
#

sub handle_end {
      my( $expat, $element ) = @_;
  
      # Wir holen einfach die gespeicherte Hashreferenz vom Stack. Dabei vertrauen wir
      # blind auf den XML-Parser und prüfen nicht, ob das gespeicherte Startelement
      # und das jetzt gemeldete Endelement tatsächlich zusammenpassen. Anders als
      # unser eigener Parser würde XML::Parser in diesem Fall Zeter und Mordio rufen.
      my $element_record = pop( @element_stack );
      print "Ein Element $element wird beendet, es begann in Zeile ",
      $$element_record{ line }, ".\n";
}

Es ist leicht zu erkennen, wie das Programm funktioniert. Wir haben zwei Funktionen namens handle_start( ) und handle_end( ) geschrieben. Diese Funktionen werden als Handler für die passenden Events beim Aufruf von new( ) registriert. Wenn die parse( )-Methode aufgerufen wird, weiß der Parser, daß er Handler für die Events »Start eines Elements« und »Ende eines Elements« besitzt. Jedesmal wenn er im folgenden ein Start-Tag findet, ruft er den ersten Handler auf und gibt ihm Informationen über das Element mit, nämlich den Elementnamen sowie die Attribute. Ganz ähnlich bewirkt das Parsen eines End-Tags den Aufruf des anderen Handlers mit den entsprechenden elementspezifischen Informationen.

Beachten Sie, daß der Parser stets auch eine Referenz auf ein Objekt namens $expat übergibt. Dabei handelt es sich um eine Instanz der Klasse XML::Parser::Expat, einer Schnittstelle zu Expat. Über diese Schnittstelle lassen sich verschiedene Informationen abfragen, die für den Prozessor von Bedeutung sein könnten, zum Beispiel die aktuelle Zeilennummer oder die aktuelle Baumtiefe. Wir benutzen das, um unsere Benutzer mit der Zeilennummer unsere Benutzer zu überraschen und die Vorzüge eines ausgereiften XML-Parsers zu demonstrieren.

Sie möchten das Programm gerne bei der Arbeit sehen? Die folgende Ausgabe erscheint, wenn man dem Programm die Kundendatenbank aus dem Beispiel XML-Dokument mit Mailingliste des SpamChucker übergibt:

Ein Element spam-document beginnt in Zeile 2!
Das Element besitzt die folgenden Attribute:
           version => 3.5
           timestamp => 2002-05-13 15:33:45
Ein Element customer beginnt in Zeile 4!
Ein Element first-name beginnt in Zeile 5!
Ein Element first-name wird beendet, es begann in Zeile 5.
Ein Element surname beginnt in Zeile 6!
Ein Element surname wird beendet, es begann in Zeile 6.
Ein Element address beginnt in Zeile 7!
Ein Element street beginnt in Zeile 8!
Ein Element street wird beendet, es begann in Zeile 8.
Ein Element city beginnt in Zeile 9!
Ein Element city wird beendet, es begann in Zeile 9.
Ein Element state beginnt in Zeile 10!
Ein Element state wird beendet, es begann in Zeile 10.
Ein Element zip beginnt in Zeile 11!
Ein Element zip wird beendet, es begann in Zeile 11.
Ein Element address wird beendet, es begann in Zeile 7.
Ein Element email beginnt in Zeile 13!
Ein Element email wird beendet, es begann in Zeile 13.
Ein Element age beginnt in Zeile 14!
Ein Element age wird beendet, es begann in Zeile 14.
Ein Element customer wird beendet, es begann in Zeile 4.
[... Der Kürze zuliebe überspringen wir die anderen customer...]
Ein Element spam-document wird beendet, es begann in Zeile 2.

In diesem Beispiel haben wir erneut den Element-Stack verwendet. Eigentlich ist es überflüssig, auch die Elementnamen zu speichern, weil eine der Methoden des XML::Parser::Expa t-Objekts Zugriff auf die jeweils aktuelle Kontextliste bietet. Die Kontextliste ist eine Liste aller Elemente, die der Parser aktuell geöffnet hat, beginnend mit dem neuesten. Ein Stack ist aber eine nützliche Möglichkeit, um zusätzliche Informationen wie zum Beispiel die Zeilennummern zu speichern. Davon abgesehen, demonstriert das, wie wir mit Hilfe von Events eigene Datenstrukturen in beliebiger Komplexität erzeugen können – gewissermaßen als »Erinnerung« an die gelesenen Teile des Dokuments.

Es gibt viele andere Eventtypen, die wir hier behandeln könnten. In diesem Beispiel ignorieren wir aber Textdaten, Kommentare oder PIs. Das Beispiel soll die prinzipielle Funktion von Events demonstrieren. Im wesentlichen ist diese Funktion stets dieselbe, so daß wir nicht unbedingt auf jeden einzelnen Typ eingehen müssen. Davon abgesehen, werden wir uns in Eventströme noch sehr viel ausführlicher mit der Verarbeitung von Events beschäftigen.

Das Thema »Verarbeitung von Events« kann nicht beendet werden, ohne ein bestimmtes Stichwort erwähnt zu haben: Die »Simple API for XML processing«, besser bekannt unter dem Kürzel SAX. Diese API ähnelt dem gerade gesehenen Event-Verarbeitungsmodell sehr stark, ist im Unterschied dazu aber ein offizieller, vom W3C unterstützter Standard. Ein vom W3C unterstützter Standard zu sein bedeutet in diesem Fall, daß es eine standardisierte, kanonische Menge definierter Events gibt. Festgelegt ist auch, wie diese Events dem verarbeitenden Programm präsentiert werden. Das Schöne an einer standardisierten Schnittstelle ist, daß man verschiedene Komponenten wie Lego-Bausteine zusammenfügen kann und das Ergebnis funktioniert. Wenn man einen bestimmten Parser nicht mag, dann verwendet man eben einen anderen. Ausgetüftelte Tools wie die Modulfamilie XML::SAX bieten sogar die automatische Auswahl eines geeigneten Parsers, je nach den geforderten Fähigkeiten. Ob man seine XML-Daten aus einer Datenbank, einer Datei oder meinetwegen auch aus Mutters Einkaufsliste bekommt, das ist egal. SAX ist ein für die Perl-Gemeinschaft sehr aufregendes Thema, weil wir lange Zeit wegen fehlender Anlehnung an Standards und wegen Sturheit kritisiert wurden. Nun kann man uns nur noch das letztere anhängen. Freuen Sie sich auf eine nette und ausführliche Erläuterung von SAX (speziell unserer bevorzugten Variante PerlSAX) in SAX.

  

  

<< zurück vor >>

 

 

 

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

Copyright © 2003 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 "Perl & XML" 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