Workshop: Arbeiten mit XML in Java, Teil 1

Das Traumpaar Java und XML

Von Neal Ford

XML hat sich beim Speichern von fast beliebigen Daten zum gängigen Dateiformat entwickelt. Es ist die Sprache, die von Firmen in der B2B-Kommunikation verwendet wird, und es hat herstellerspezifische Texdatei-Formate nahezu ersetzt (Beispiele: Einsatzdeskriptoren für EJB, Web-Konfiguration im WAR-Dateiformat, Dateierstellungs-Syntax für Ant, u. a.). Desweiteren taucht es immer häufiger in Datenbanken auf. Da es sich um einen offenen Standard handelt, wird eine Bindung der Unternehmen an herstellerspezifische Lösungen vermieden. Da der Einsatz von XML immer größere Verbreitung findet, gewinnen Werkzeuge, die dieses Format verstehen können, zunehmend an Bedeutung. Dieser Trend blieb in der Java-Gemeinde nicht unbemerkt. Es wurden sogar schon verschiedene APIs entwickelt, um Entwickler beim Arbeiten mit XML zu unterstützen. Dieser Artikel soll im Schnelldurchgang einen Überblick über einige der häufiger verwendeten APIs und Techniken verschaffen, die für das Arbeiten mit XML-Dokumenten entwickelt wurden.

Ein kurzer Überblick zu XML

XML (Extensible Markup Language) steht in engem Zusammenhang
zu SGML (Standard Graphical Markup Language) und ist der logische Nachfolger von
HTML. Eines der Probleme bei HTML besteht darin, dass es nur für die Präsentation
konzipiert wurde – es besteht keine Möglichkeit, in einer standardisierten Weise
innerhalb des Dokuments einen Sinn zu erhalten. Wenn man z. B. im Internet eine
Suche nach Cardinal ausführt, erhält man Fundstellen zu Baseballspielern, Zahlenarten
und katholischen Internetseiten. Es gibt für HTML keine Möglichkeit, die verschiedenen
Kategorien für Cardinal zu unterscheiden. In der Frühzeit von HTML waren die Entwickler
bei Syntaxregeln nicht sehr streng. Nehmen wir z. B. folgende HTML-Konstruktion:

This is bold and italic.

Browser hätten damit keine Probleme, auch wenn es technisch kein zulässiges HTML
ist. Die Tags sollen aufeinander abgestimmt sein: so sollte der Tag vor dem Tag
erscheinen. Da die frühen Browser dies zuließen, muss die moderne Browsergeneration
dies ebenfalls erlauben (der Abwärts-kompatibilität zuliebe). Es gibt Hunderte
solcher kleinen, harmlosen Regelverletzungen, die heute das Schreiben eines Browsers
zu einer gewaltigen Aufgabe machen. Der Entwickler muss sich nicht nur um zulässiges
HTML kümmern, sondern auch um all die Varianten, die durch diese Ausnahmen erzeugt
werden. XML löst diese Probleme, indem es ein Dokumentformat erzeugt, das auf
anwenderspezifischen Tags und streng verbindlichen Syntaxregeln basiert. HTML
verfügt über eine bestimmte Menge an Standard-Tags, die jeder Browser versteht.
XML ermöglicht es dem Autor des Dokuments, seine eigenen Tags zu erzeugen und
eine spezifische Bedeutung mit ihnen zu verbinden. Anders als HTML ist XML eher
ein Datendokumentformat als ein Präsentationsformat. Ein XML-Beispieldokument
finden Sie in Listing 1 (dieses und alle weiteren Listings finden Sie auf der
beiliegenden CD). In diesem Fall wurden anwenderspezifische Tags definiert, die
für die Person oder Anwendung, die dieses Dokument lesen soll, eine besondere
Bedeutung enthalten. Beachten Sie, dass dies in einem Browser nicht in besonderer
Weise angezeigt wird – XML ist eine Datendarstellungssprache. Jedoch kann eine
Anwendung, die in der Lage ist, XML zu parsen, aus diesem Dokument in strukturierter
Weise Informationen entnehmen. XML ist an sich ein sehr weitläufiges Thema und
würde den Rahmen dieses Artikels sprengen (und offenbar auch eine Unmenge von
Büchern füllen). Unsere Absicht ist es, zu zeigen, wie ein XML-Dokument aussieht,
so dass wir es parsen können.

Beschaffung eines Parsers

Bevor Sie XML parsen können, müssen
Sie sich einen Parser besorgen. Es gibt eine Reihe von Parsern, die zum Download
und/oder Kauf zur Verfügung stehen. In der folgenden Tabelle sind einige der verfügbaren
Parser mit der jeweiligen Download-Adresse aufgeführt.

Parser Bezugsquelle
Apache Xerces xml.apache.org
IBM XML4J alphaworks.ibm.com/tech/xml4j
OpenText Aelfred www.opentext.com/services
/content_management_services
/xml_sgml_solutions.html#aelfred_and_sax
Oracle XML Parser technet.oracle.com/tech/xml
OpenXML www.openxml.org
James Clark’s XP www.jclark.com/xml/xp
Lark and Larval www.textuality.com/Lark

Ein Warnhinweis ist hier angebracht: Microsoft produziert einen XML-Parser
unter dem Namen MSXML. Bei Verfassen dieses Artikels war es kein Standard-XML-Parser,
sondern er basiert auf Microsoft-spezifischer Technologie und sollte nicht als
standardbasierter Parser betrachtet werden. Der für diesen Artikel verwendete
Parser ist Xerces von Apache, einer der am häufigsten verwendeten verfügbaren
Parser (tatsächlich ist es die Kernmaschine, die vom XML4J-Parser von IBM benutzt
wird). Er ist offener Quellcode und frei erhältlich. Die Beispiele in diesem Artikel
können jedoch leicht geändert werden, um in einem anderen standardbasierten XML-Parser
zu funktionieren. Nachdem Sie Ihren Parser heruntergeladen haben, sollten Sie
die Dateien auf Ihre Festplatte extrahieren. Normalerweise wird der Parser in
eine JAR-Datei integriert, die Sie Ihrem Klassenpfad hinzufügen sollten.Kennen
sollte man auch JAXP, den Java-API für XML-Parsing von Sun. Es ist ein API für
die Verwendung von XML-Parsern in Ihrer Anwendung, ohne genauere Angaben über
den speziellen Parser, den Sie gerade verwenden. Es handelt sich um einen Satz
von Schnittstellen, an die XML-Parser andocken. Er erlaubt Ihnen, problemlos von
einem Parser zum nächsten zu wechseln, ohne am Quellcode Änderungen vorzunehmen.
Er ist per Download von der JavaSoft-Internetadresse erhältlich.

Der SAX-API

Es gibt zwei unterschiedliche wichtigere APIs zum Parsen von XML mit Java. Der
erste läuft unter dem Namen SAX (Simple API for XML). SAX stellt zu einem gegebenen
Parser eine ereignisgesteuerte Schnittstelle zur Verfügung. Beachten Sie hierbei,
dass SAX kein Parser ist, wie häufig fälschlicherweise angenommen wird. Es handelt
sich vielmehr um einen Standardsatz von Schnittstellen, der über dem Parser andockt,
um eine konsistente, nicht parserspezifische Methode zur Abwicklung des Parsens
zur Verfügung zu stellen. SAX beruht auf Callback-Methoden, die in jeder Stufe
des Parsing-Prozesses auftreten. SAX arbeitet über einen Schnittstellensatz, den
der Entwickler implementieren muss. Die primäre Schnittstelle für das Parsing
ist die DocumentHandler-Schnittstelle, die Sie in Listing 2 sehen. Diese Schnittstelle
enthält abstrakte Methoden, um alle möglichen Parsing-Ereignisse abzuwickeln,
die beim Parsen des XML-Dokuments auftreten. Um ein Dokument unter Verwendung
von SAX zu parsen, müssen Sie eine Klasse erzeugen, die diese Schnittstelle implementiert
(und übersteuerte Methoden für jede der oben erwähnten abstrakten Methoden finden,
wobei zumindest eine Teil-implementierung erzeugt wird), und Ihre Klasse mit dem
Parser registrieren. Dieser Prozess wird in den folgenden Abschnitten besprochen.

Erzeugen eines DocumentHandler

Zuerst werden wir uns mit der Klasse befassen, die die im Listing 3 aufgeführte
DocumentHandler-Schnittstelle implementiert. Sie druckt eine Statusmeldung für
jedes Dokumentelement aus, das aufgesucht wird. Offensichtlich könnten Sie für
jedes Ereignis eine beliebige gewünschte Funktionalität hinzufügen. Es ist eine
konkrete Klasse – alle erforderlichen Methoden aus der DocumentHandler-Schnittstelle
werden erfüllt. Danach werden wir auf die Klasse eingehen, die diesen DocumentHandler
verwendet. Sie ist in Listing 4 dargestellt. Die Klasse beinhaltet zwei Methoden:
performTest() und main(). Die Methode performTest() erledigt das eigentliche Parsen.
Beachten Sie dabei, dass wir ein neues Objekt SAXParser() instanzieren und den
TestContentHandler über die Methode setContentHandler() mit ihm assoziieren. Um
das Dokument tatsächlich zu parsen, rufen wir die Methode parse() des Parsers
auf. Diese Methode parst das Dokument und führt dabei die in unserer DocumentHandler-Klasse
vorhandenen Callbacks aus, sobald sie auf die einzelnen Elemente trifft.

Erzeugen eines ErrorHandler

Eine andere Schnittstelle, die über SAX verwendet werden kann, ist die ErrorHandler-Schnittstelle,
die zur Fehlerbehandlung verwendet wird. Diese Schnittstelle ist viel kleiner
und definiert das Verhalten bei Warnmeldungen und Fehlern. Tatsächlich gibt es
nur sehr wenige Warnmeldungen in SAX – das Parsen wird nämlich fortgesetzt, außer
beim Auftreten eines fatalen Fehlers; die meisten Fehlerzustände werden über den
Fehler-Callback behandelt. Ein Beispiel für eine Klasse, die diese Schnittstelle
implementiert, finden Sie in Listing 6. Die einzige Änderung, die zum Hinzufügen
der Fehlerbehandlung für die oben genannte Anwendung erforderlich ist, ist der
Aufruf von setErrorHandler(), der eine instanzierte Version dieser Klasse durchläuft.
Die einzigen Codezeilen, die zur Verwendung dieses Fehlerbehandlers hinzugefügt
werden, erscheinen dann direkt nach dem Aufruf von setContentHandler().

ErrorHandler errorHandler =
new TestErrorHandler();
parser.setErrorHandler(errorHandler);
Verwendung von Factories

Eine unerwünschte Eigenschaft der obigen Testanwendung ist die
Art und Weise, wie der Parser instanziert wird. Wir erzeugen den Parser, indem
wir die spezifische Parserklasse importieren und ihn direkt instanzieren. Dies
ist nicht erwünscht, da es dieses Programm immer an denselben Parser bindet –
denjenigen, zu dem ein fester Verweis besteht. Die typische Methode, um diese
Art fester Verweise in Java zu vermeiden, besteht in der Verwendung des Factory-Entwurfsmusters.
Dieses ist tatsächlich so gebräuchlich, dass in den Standard-SAX-APIs zur Unterstützung
der Entwickler eine spezielle Factory-Klasse enthalten ist. Die Klasse SAXParserTest
wurde in Listing 7 geändert, um Verweise zu einer bestimmten Parser-Implementierung
zu entfernen. Beachten Sie, dass der fest codierte Verweis zum Parser aus der
Importanweisung entfernt wurde, und die Reader-Klasse jetzt über die XMLReaderFactory
instanziert wird. In diesem Fall wird der Parser als fest codierte Zeichenkette
eingebunden. Beachten Sie jedoch, dass dieser Klassenverweis dynamisch geladen
werden kann, entweder durch eine Eigenschaften-Datei oder über die Befehlszeile.
Das Ergebnis ist eine Anwendung, deren Parser problemlos durch einen anderen ersetzt
werden kann, ohne dass Änderungen am Code (und eine Rekompilierung) zwingend notwendig
werden. Der SAX API eignet sich sehr gut für XML-Parsingroutinen, die sequenziell
erfolgen können, wenn das Dokument verarbeitet wird. Beachten Sie, dass die Callbacks
der Reihe nach erfolgen – es gibt keine Möglichkeit der Rückwärtsverarbeitung,
um an einen vorher liegenden Punkt zu gelangen. Es gibt einige Situationen, in
denen es wünschenswert ist, das gesamte Dokument in den Speicher laden zu können,
um es zu bearbeiten. In diesem Moment ist DOM API ideal.

Das DOM-API

DOM
steht für Document Object Model, und dieser Begriff wird in mehreren verschiedenen
Zusammenhängen verwendet. Hier bezieht er sich auf ein speicherinternes Modell
des XML-Dokuments. Die DOM-APIs definieren eine weitere Methode zur Ansicht eines
XML-Dokuments, diesmal eher als Ganzes anstatt der Callbacks, die bei der Verarbeitung
des Dokuments auftreten. Eine Beispielanwendung, die zeigt, wie man den DOM-Parser
verwendet, ist in Listing 8 aufgeführt. XML-Dokumente definieren Daten mit Baumstruktur,
und der DOM-Parser nutzt diese Eigenschaft. Normalerweise verwendet der Code zum
Parsen eines Dokuments, der DOM verwendet, eine rekursive Methode, um alle Teile
des Dokuments nacheinander aufzusuchen. Dies wird vom DOM-API gut unterstützt,
da jedes Element eine Art von Knoten darstellt. Die Knotenklasse verfügt über
eine Methode, die die Art des Knotens rückmeldet, der gerade aufgesucht wird.
Der DOM-API ist für sprachenübergreifendes Arbeiten konzipiert, und es gibt DOM-APIs,
die mit C++, Perl und mehreren anderen Sprachen arbeiten. Da er sprachenübergreifend
unterstützt wird, werden alle Datenstrukturen, die für das Parsen von XML erforderlich
sind, als Teil des DOM-API definiert. So ist z. B. die Klasse NodeList dafür konzipiert,
eine Liste von Knoten zu enthalten, und der node.getChildNodes() gibt eine NodeList
aus. Um mit dem DOM-API vertraut zu werden, müssen Sie daher alle Utility-Klassen
kennen lernen, die dieser definiert. DOM wird verwendet, wenn das gesamte XML-Dokument
im Speicher verarbeitet werden muss. Da DOM für XML eine baumartige Struktur definiert,
ist es einfach, eine Anwendung zu erstellen, die den DOM-Baum benutzt, um eine
grafische Darstellung des XML-Dokuments aufzubauen. Tatsächlich macht eine der
mit Xerces zur Verfügung gestellten Beispielanwendungen genau dies. Zum Beispiel
baut TreeView das durchlaufene XML-Dokument zu einem DOM-Baum im Speicher auf
und zeigt die Ergebnisse in einem Swing JTree. Diese Anwendung ist in Abpictureung
1 dargestellt.

Abb. 1: Von TreeView zu einem DOM-Baum aufgebautes XML-Dokument

Wie Sie sehen können, ist DOM für diese Art der Darstellung
gut geeignet. Jedoch hat DOM einige Nachteile. In erster Linie benötigt DOM genügend
Speicher, um den gesamten Baum in den Speicher zu laden – es gibt keine Möglichkeit
im API, nur einen Teil des Baums zu laden. DOM ist daher sehr speicherintensiv,
besonders bei großen Dokumenten.

SAX oder DOM?

Da nun also zwei verschiedene
Parser existieren – mit völlig verschiedenen Philosophien -, bleibt die Frage,
welchen man verwenden soll. Das hängt natürlich vom Einzelfall ab. Wenn Sie nur
einmal durch das Dokument parsen müssen und sich dabei jedes Element der Reihenfolge
nach vornehmen, werden Sie sich für SAX entscheiden. Es ist viel weniger speicherintensiv
und kann demnach viel größere Dokumente verarbeiten. Es ist eine gute Wahl, wenn
Sie die Konfigurationsinformationen in einem XML-Dokument aufbewahren
und es nur einmal lesen müssen, um die entsprechenden Anwendungsdaten auszufüllen.
Wenn Sie dagegen die Informationen verarbeiten müssen, die in dem XML-Dokument
erscheinen und sie womöglich zurückschreiben müssen, ist DOM überlegen. Es ermöglicht
Ihnen, die Elemente zu manipulieren, neue Elemente hinzuzufügen, usw. und den
aktualisierten DOM-Baum zurück auf die Festplatte zu schreiben. Da DOM jedoch
das gesamte Dokument in den Speicher lesen muss, nimmt seine Leistungsfähigkeit
bei großen Dokumenten ab. Für welche Parser-Philosophie Sie sich entscheiden,
hängt davon ab, wie Sie ihn verwenden müssen. Es ist außerdem nicht selten, dass
beide in derselben Anwendung zum Einsatz kommen. In Teil 1 dieser Serie wurden
die wichtigsten APIs zum Parsen von XML vorgestellt. In Teil 2 werde ich auf das
Thema Konvertierung von Dokumenten in verschiedene Formate näher eingehen. Ich
werde auch einige Alternativen zum regulären DOM und SAX besprechen. Neal Ford
ist Vice President Technology bei der in Atlanta ansässigen DSW Group. Er hat
bereits verschiedene Veröffentlichungen zu Java und Delphi erarbeitet und spricht
regelmäßig auf internationalen Konferenzen, so auch der JAX2002 oder der Entwickler
Konferenz.

Geschrieben von
Von Neal Ford
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.