APIs für XML

Die Basis für Programme, die XML verarbeiten, sind Parser, die validierend und nichtvalidierend sein können. Für die Programmentwicklung benutzt man aber meist APIs, die von den Parsern abstrahieren. Dabei gibt es zwei dominierende Typen von APIs: Ereignisbasierte und baumbasierte.

Parser

Parser sind, wie gesagt, für die APIs von untergeordneter Bedeutung. Es ist aber wichtig, zu wissen, dass es zwei verschiedene Atren von Parsern gibt: validierende und nichtvalidierende. Ein validierender Parser liest das Dokument, die DTD und alle externen Entitäten (salopp ausgedrückt die XML-Version von #include-Dateien) und berücksichtig alle darin definierten Dafaultwerte von Arrtibuten und allgemeinen Entitäten. Außerdem testet er, dass das Dokument tatsächlich den in der DTD aufgestellen formalen Anforderungen entspricht (daher hat er auch seinen Namen.

Ein nichtvalidierender Parser führt diese Tests nicht durch. Er kann aber trotzdem die DTD lesen und dort definierte Entitäten berücksichtigen, und auch externe Entitäten lesen. Diese Freiheit kann dazu führen, dass das gleiche Dokument sich einem Programm sehr verschieden präsentiert, je nachdem, mit was für einem Parser es gelesen wurde. Für den an Details interessierten Leser: Es gibt wenigstens zwei Versuche, eine Lösung für dieses Problem zu finden. Der eine ist [Ext. Link]SML, der auch durch viele andere Dinge motivierte Vorschlag, XML radikal zu vereinfachen. SML soll eine absolut minimale Teilsprache von XML werden, die praktisch nur richtig verschachtelte öffnende und schließende Tags ohne gemischten Inhalt und in Unicode-Kodierung zulässt. Der andere, [Ext. Link]Common XML, beschreibt dagegen, wie man im Rahmen des "normalen" XML diese Probleme durch geeignete Beschränkung bei der Wahl der benutzten Features umgehen kann.

Ereignisbasierte APIs -- SAX

Das Vorbild für die meisten ereignisbasierten APIs ist in Java definierte die Simple API for XML [Ext. Link]SAX, weshalb ereignisbasiert und SAX-artig oft synonym gebraucht werden. Bei einer ereignisbasierten API gibt es zwei wichtige Programmkomponenten: Einen Treiber der Ereignisse erzeugt, und ein Handler, der diese Ereignisse verarbeitet. In einen typischen Programm ist der SAX-Treiber ein XML-Parser, und der Handler enthält die eigentliche Anwendungslogik des Programms. Wenn der Parser dann ein Dokument liest, wird jeder Teil des Dokuments in ein Ereignis übersetzt und an den Handler übergeben. In objektorientierten Sprachen sin Treiber und Handler Objekte, und er Treiber schickt dem Handler den Ereignissen entsprechende Nachrichten (Smalltalk-Schule) bzw. ruft die entsprechenden Objektmethoden auf (SIMULA-Schule). In C ist der Handler einfach ein struct mit Funktionspointern, und der Treiber ruft die Funktionen auf.

Der Treiber muss kein Parser sein, sondern kann ein beliebiges Stück Code sein, das einem Handler die Ereignisse mitteilen kann. Es gibt zum Beispiel Multiplexer, die sich bei einem SAX-Treiber als Handler registrieren, und dann alle Ereignisse, die sie mitgeteilt erhalten, an mehrere andere Handler weiterleiten, die sich bei ihnen registriert haben. Im Prinzip kann auch eine Funktion, die direkt Ereignisse erzeugt, als Treiber auftreten, dem Handler ist das egal. Das Beispieldokument könnte etwas Vereinfacht in Perl auszugsweise so aussehen:

sub ein_SAX_Treiber {
  my $handler=shift;

  $handler->start_document();
  $handler->start_element((Name => "sect"));
  $handler->start_element((Name => "title",
                           Attributes => (id => "start")));
  $handler->characters((Data => "Generisches Mark"));
  $handler->characters((Data => "up"));
  $handler->end_element((Name => "title"));
  [...]
  $handler->end_element((Data => "sect"));
  $handler->end_document();
}

Aus didaktischen Gründen habe ich dabei die Daten für den Inhalt des title-Elements auf zwei Aufrufe von characters verteilt. Es gibt keine Gewähr dafür, dass zusammenhängende Textabschnitte in einem Aufruf übergeben werden, ein Treiber kann sie auch in Blöcken zu je 16 Zeichen übergeben. Wie oft bei XML hat auch bei SAX die Einfachheit der Implementation Vorrang vor der Einfachheit der Benutzung.

Baumbasierte APIs -- DOM

Ein zum ereignisbasierten Ansatz kompämentärer wird von den baumbasierten APIs repräsentiert. Teile eines Dokuments werden dabei nicht als Ereignisse, sondern als Knoten in einem Baum betrachtet. Das paradigmatische Beispiel ist das [Ext. Link]Document Object Model DOM, ein offizieller Standard des W3C. Bei baumbasierten APIs wird (normalerweise) das gesamte Dokument als ein Baum im Speicher gehalten, und die API stellt Funktionen zur Verfügung, um in diesem Baum zu navigieren und Elemente nach bestimmten Kriterien zu suchen. Auch bei DOM gibt es keine Gewähr dafür, dass logisch oder auch physikalisch zusammengehörende Textabschnitte als ein einziger Textknoten dargestellt werden.

Bis auf Kommentare sieht die Baumdarstellung des Beispieldokuments so aus:


[DOM-Grafik]

Das einfache Perl-Skript, das dise Grafik erstellt hat, indem es sich rekursiv durch den Baum hangelt, ist auch ein nettes Beispiel für die Benutzung von DOM.

Andere APIs

Viele reale Implementationen bieten beide APIs an. Für eine DOM-Implementation ist es oft sinnvoll, auf einem SAX-Parser aufzusetzen und die eigenen Routinen, die aus den Ereignissen einen Baum aufbauen, als Standardhandler zu registrieren. Dieser kann dann von einem Benutzer, der DOM nicht benutzen will, durch einen eigenen Handler ersetzt werden. Umgekehrt kann auch eine DOM-Implementation als SAX-Treiber fungiern, indem der Baum einfach rekursiv durchlaufen wird und man an jedem Knoten die entsprechenden Ereignisse erzeugt.

Daneben gibt es aber auch noch viele andere APIs, die den Paradigmen SAX und DOM mehr oder weniger ähnlich sind, aber unterschiedliche Schwerpunkte bei Einfachkeit, Ressourcenbedarf oder Flexibilität setzen. Es ist vielleicht nicht völlig überraschend, dass es gerade bei Perl eine große Vielfalt von APIs gibt, und mit dem [Ext. Link]Ways to Rome-Dokument gibt es in dem Umfeld auch einen interessante Vergleich zwischen den verschiedenen Herangehensweisen an XML.

Die meisten verbreiteten Implementationen stehen den Standards aber recht nahe.

Florian Hars <florian@hars.de>, 2007-10-15 (orig: 2000-06-17)