Mittwoch, 19. November 2008





April 2006
aus Java Magazin Ausgabe: 04.2002
Das Traumpaar Java und XML
Workshop: Arbeiten mit XML in Java, Teil 1
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:

<b><i>This is bold and italic.</b></i>

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.
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 Abbildung 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.


    Hat Ihnen dieser Artikel gefallen? Dann abonnieren Sie das Entwickler Magazin direkt über unser Online-Formular.

zur vorherigen Seite
zurück
an den Anfang der Seite
nach oben
Diesen Artikel drucken
drucken
Diesen Artikel weiterempfehlen
empfehlen




Software & Support Verlag GmbH