XML in Java programmieren, Teil 1

Sie verstehen also (mehr oder weniger), wie Sie Ihre Daten in XML darstellen würden, und Sie möchten XML verwenden, um viele Ihrer Datenverwaltungsprobleme zu lösen. Sie sind sich jedoch nicht sicher, wie Sie XML mit Ihren Java-Programmen verwenden sollen.

TEXTBOX: TEXTBOX_HEAD: XML in Java programmieren: Lesen Sie die ganze Serie!

  • Teil 1. Verwenden Sie die Simple API for XML (SAX), um XML in Java einfach zu verarbeiten
  • Teil 2. Erfahren Sie mehr über die SAX- und XML-Validierung anhand anschaulicher Beispiele
  • Teil 3. DOMination: Übernehmen Sie mit dem Document Object Model die Kontrolle über strukturierte Dokumente

: END_TEXTBOX

Dieser Artikel ist eine Fortsetzung meines Einführungsartikels "XML für den absoluten Anfänger" in der April 1999-Ausgabe von JavaWorld (die URL finden Sie im Abschnitt Ressourcen unten). Dieser Artikel beschrieb XML; Ich werde nun auf dieser Beschreibung aufbauen und im Detail zeigen, wie eine Anwendung erstellt wird, die die einfache API für Java (SAX) verwendet, eine leichte und leistungsstarke Standard-Java-API für die Verarbeitung von XML.

Der hier verwendete Beispielcode verwendet die SAX-API, um eine XML-Datei zu lesen und eine nützliche Struktur von Objekten zu erstellen. Wenn Sie diesen Artikel fertiggestellt haben, können Sie Ihre eigenen XML-basierten Anwendungen erstellen.

Die Tugend der Faulheit

Larry Wall, der verrückte geniale Schöpfer von Perl (der zweitgrößten Programmiersprache, die es gibt), hat erklärt, dass Faulheit eine der "drei großen Tugenden" eines Programmierers ist (die anderen beiden sind Ungeduld und Hybris). Faulheit ist eine Tugend, weil ein fauler Programmierer fast jede Anstrengung unternimmt, um Arbeit zu vermeiden, und sogar allgemeine, wiederverwendbare Programmierframeworks erstellt, die wiederholt verwendet werden können. Das Erstellen solcher Frameworks ist mit viel Arbeit verbunden, aber die Zeitersparnis für zukünftige Aufgaben macht den anfänglichen Aufwand mehr als wett. Mit den besten Frameworks können Programmierer erstaunliche Dinge mit wenig oder gar keiner Arbeit erledigen - und deshalb ist Faulheit tugendhaft.

XML ist eine Technologie, die es dem tugendhaften (faulen) Programmierer ermöglicht. Ein einfacher XML-Parser erledigt viel Arbeit für den Programmierer, erkennt Token, übersetzt codierte Zeichen, erzwingt Regeln für die XML-Dateistruktur, überprüft die Gültigkeit einiger Datenwerte und ruft gegebenenfalls anwendungsspezifischen Code auf. Tatsächlich hat die frühe Standardisierung in Kombination mit einem hart umkämpften Markt zahlreiche frei verfügbare Implementierungen von Standard-XML-Parsern in vielen Sprachen hervorgebracht, darunter C, C ++, Tcl, Perl, Python und natürlich Java.

Die SAX-API ist eine der einfachsten und leichtesten Schnittstellen für den Umgang mit XML. In diesem Artikel verwende ich die XML4J-Implementierung von SAX von IBM. Da die API jedoch standardisiert ist, kann Ihre Anwendung jedes Paket ersetzen, das SAX implementiert.

SAX ist eine ereignisbasierte API, die nach dem Callback-Prinzip arbeitet. Ein Anwendungsprogrammierer erstellt normalerweise ein SAX- ParserObjekt und übergibt ihm sowohl Eingabe-XML als auch einen Dokumenthandler, der Rückrufe für SAX-Ereignisse empfängt. Der SAX Parserkonvertiert seine Eingabe in einen Strom von Ereignissen , die strukturellen Merkmalen der Eingabe entsprechen, wie z. B. XML-Tags oder Textblöcke. Jedes Ereignis wird an die entsprechende Methode eines vom Programmierer definierten Dokumenthandlers übergeben, der die Rückrufschnittstelle implementiert org.xml.sax.DocumentHandler. Die Methoden in dieser Handlerklasse führen die anwendungsspezifische Funktionalität während der Analyse aus.

Stellen Sie sich beispielsweise vor, ein SAX-Parser empfängt ein Dokument, das das in Listing 1 gezeigte winzige XML-Dokument enthält. (Siehe Ressourcen für die XML-Datei.)

 Ogden Nash Flöhe Adam Had 'em.  

Listing 1. XML für ein kurzes Gedicht

Wenn der SAX-Parser auf das Tag stößt , ruft er das benutzerdefinierte DocumentHandler.startElement()mit der Zeichenfolge POEMals Argument auf. Sie implementieren die startElement()Methode, um das zu tun, was die Anwendung tun soll, wenn a POEMbeginnt. Der Strom von Ereignissen und daraus resultierenden Aufrufen für das oben genannte XML-Element ist in Tabelle 1 unten aufgeführt.

Tabelle 1. Die Reihenfolge der Rückrufe, die SAX beim Parsen von Listing 1 erzeugt
Gegenstand gefunden Parser-Rückruf
{Beginn des Dokuments} startDocument()
startElement("POEM", {AttributeList})
"\ n" characters("\n...", 6, 1)
startElement("AUTHOR", {AttributeList})
"Ogden Nash" characters("\n...", 15, 10)
endElement("AUTHOR")
"\ n" characters("\n...", 34, 1)
startElement("TITLE", {AttributeList})
"Flöhe" characters("\n...", 42, 5)
endElement("TITLE")
"\ n" characters("\n...", 55, 1)
startElement("LINE", {AttributeList})
"Adam" characters("\n...", 62, 4)
endElement("LINE")
startElement("LINE", {AttributeList})
"Hatte sie." characters("\n...", 67, 8)
endElement("LINE")
"\ n" characters("\n...", 82, 1)
endElement("POEM")
{Ende des Dokuments} endDocument()

Sie erstellen eine Klasse, die implementiert wird, DocumentHandlerum auf Ereignisse zu reagieren, die im SAX-Parser auftreten. Diese Ereignisse sind keine Java-Ereignisse, wie Sie sie möglicherweise aus dem Abstract Windowing Toolkit (AWT) kennen. Dies sind Bedingungen, die der SAX-Parser beim Parsen erkennt, z. B. der Beginn eines Dokuments oder das Auftreten eines schließenden Tags im Eingabestream. Wenn jede dieser Bedingungen (oder Ereignisse) auftritt, ruft SAX die Methode auf, die der Bedingung in ihrer entspricht DocumentHandler.

Der Schlüssel zum Schreiben von Programmen, die XML mit SAX verarbeiten, besteht darin, herauszufinden, was als DocumentHandlerReaktion auf einen Strom von Methodenrückrufen von SAX zu tun ist. Der SAX-Parser kümmert sich um alle Mechanismen zum Identifizieren von Tags, Ersetzen von Entitätswerten usw., sodass Sie sich auf die anwendungsspezifische Funktionalität konzentrieren können, die die in XML codierten Daten verwendet.

Tabelle 1 zeigt nur Ereignisse, die Elementen und Zeichen zugeordnet sind. SAX enthält auch Funktionen zum Behandeln anderer struktureller Merkmale von XML-Dateien, z. B. Entitäten und Verarbeitungsanweisungen, die jedoch den Rahmen dieses Artikels sprengen.

Der kluge Leser wird feststellen, dass ein XML-Dokument als Baum typisierter Objekte dargestellt werden kann und dass die Reihenfolge des Ereignisstroms, der DocumentHandlerdem präsentiert wird, einer Reihenfolge des Dokumentbaums in der Reihenfolge entspricht. (Es ist nicht unbedingt erforderlich, diesen Punkt zu verstehen, aber das Konzept eines XML-Dokuments als Baumdatenstruktur ist nützlich für komplexere Arten der Dokumentverarbeitung, die in späteren Artikeln dieser Reihe behandelt werden.)

Der Schlüssel zum Verständnis der Verwendung von SAX ist das Verständnis der DocumentHandlerBenutzeroberfläche, auf die ich als Nächstes eingehen werde.

Passen Sie den Parser mit org.xml.sax.DocumentHandler an

Da die DocumentHandlerSchnittstelle für die Verarbeitung von XML mit SAX so zentral ist, lohnt es sich zu verstehen, was die Methoden in der Schnittstelle bewirken. Ich werde die wesentlichen Methoden in diesem Abschnitt behandeln und diejenigen überspringen, die sich mit fortgeschritteneren Themen befassen. Denken Sie daran, dass DocumentHandleres sich um eine Schnittstelle handelt. Die von mir beschriebenen Methoden sind also Methoden, die Sie implementieren, um anwendungsspezifische Funktionen zu verarbeiten, wenn das entsprechende Ereignis eintritt.

Initialisierung und Bereinigung von Dokumenten

Für jedes analysierte Dokument ruft der SAX XML-Parser die DocumentHandlerSchnittstellenmethoden startDocument()(vor Beginn der Verarbeitung aufgerufen) und endDocument()(nach Abschluss der Verarbeitung aufgerufen) auf. Mit diesen Methoden können Sie Ihre initialisieren DocumentHandler, um sie auf den Empfang von Ereignissen vorzubereiten und die Ausgabe nach Abschluss der Analyse zu bereinigen oder zu produzieren. endDocument()ist besonders interessant, da es nur aufgerufen wird, wenn ein Eingabedokument erfolgreich analysiert wurde. Wenn das Parsereinen schwerwiegenden Fehler erzeugt, bricht es einfach den Ereignisstrom ab und beendet das Parsen und endDocument()wird niemals aufgerufen.

Tags verarbeiten

Der SAX-Parser ruft startElement()immer dann auf, wenn er auf ein offenes Tag endElement()stößt und wenn er auf ein geschlossenes Tag stößt. Diese Methoden enthalten häufig den Code, der den größten Teil der Arbeit beim Parsen einer XML-Datei erledigt. startElement()Das erste Argument ist eine Zeichenfolge, bei der es sich um den Tag-Namen des gefundenen Elements handelt. Das zweite Argument ist ein Objekt vom Typ AttributeList, eine im Paket definierte Schnittstelle org.xml.sax, die einen sequentiellen oder zufälligen Zugriff auf Elementattribute nach Namen ermöglicht. (Sie haben zweifellos schon Attribute in HTML gesehen; in der Zeile

BORDER

Since SAX doesn't provide any information about the context of the elements it encounters (that appears inside in Listing 1 above, for example), it is up to you to supply that information. Application programmers often use stacks in startElement() and endElement(), pushing objects onto a stack when an element starts, and popping them off of the stack when the element ends.

Process blocks of text

The characters() method indicates character content in the XML document -- characters that don't appear inside an XML tag, in other words. This method's signature is a bit odd. The first argument is an array of bytes, the second is an index into that array indicating the first character of the range to be processed, and the third argument is the length of the character range.

It might seem that an easier API would have simply passed a String object containing the data, but characters() was defined in this way for efficiency reasons. The parser has no way of knowing whether or not you're going to use the characters, so as the parser parses its input buffer, it passes a reference to the buffer and the indices of the string it is viewing, trusting that you will construct your own String if you want one. It's a bit more work, but it lets you decide whether or not to incur the overhead of String construction for content pieces in an XML file.

The characters() method handles both regular text content and content inside CDATA sections, which are used to prevent blocks of literal text from being parsed by an XML parser.

Other methods

Es gibt drei weitere Methoden in der DocumentHandlerSchnittstelle: ignorableWhitespace(), processingInstruction(), und setDocumentLocator(). ignorableWhitespace()meldet das Auftreten von Leerzeichen und wird normalerweise nicht in nicht validierten SAX-Parsern verwendet (wie dem, den wir für diesen Artikel verwenden); processingInstruction()behandelt die meisten Dinge innerhalb and ?> delimiters; and setDocumentLocator() is optionally implemented by SAX parsers to give you access to the locations of SAX events in the original input stream. You can read up on these methods by following the links on the SAX interfaces in Resources.

Implementing all of the methods in an interface can be tedious if you're only interested in the behavior of one or two of them. The SAX package includes a class called HandlerBase that basically does nothing, but can help you take advantage of just one or two of these methods. Let's examine this class in more detail.

HandlerBase: A do-nothing class

Often, you're only interested in implementing one or two methods in an interface, and want the other methods to simply do nothing. The class org.xml.sax.HandlerBase simplifies the implementation of the DocumentHandler interface by implementing all of the interface's methods with do-nothing bodies. Then, instead of implementing DocumentHandler, you can subclass HandlerBase, and only override the methods that interest you.

For example, say you wanted to write a program that just printed the title of any XML-formatted poem (like TitleFinder in Listing 1). You could define a new DocumentHandler, like the one in Listing 2 below, that subclasses HandlerBase, and only overrides the methods you need. (See Resources for an HTML file of TitleFinder.)

012 /** 013 * SAX DocumentHandler class that prints the contents of "TITLE" element 014 * of an input document. 015 */ 016 public class TitleFinder extends HandlerBase { 017 boolean _isTitle = false; 018 public TitleFinder() { 019 super(); 020 } 021 /** 022 * Print any text found inside a  element. 023 */ 024 public void characters(char[] chars, int iStart, int iLen) { 025 if (_isTitle) { 026 String sTitle = new String(chars, iStart, iLen); 027 System.out.println("Title: " + sTitle); 028 } 029 } 030 /** 031 * Mark title element end. 032 */ 033 public void endElement(String element) { 034 if (element.equals("TITLE")) { 035 _isTitle = false; 036 } 037 } 038 /** 039 * Find contents of titles 040 */ 041 public static void main(String args[]) { 042 TitleFinder titleFinder = new TitleFinder(); 043 try { 044 Parser parser = ParserFactory.makeParser("com.ibm.xml.parsers.SAXParser"); 045 parser.setDocumentHandler(titleFinder); 046 parser.parse(new InputSource(args[0])); 047 } catch (Exception ex) { 048 ; // OK, so sometimes laziness *isn't* a virtue. 049 } 050 } 051 /** 052 * Mark title element start 053 */ 054 public void startElement(String element, AttributeList attrlist) { 055 if (element.equals("TITLE")) { 056 _isTitle = true; 057 } 058 } 

Listing 2. TitleFinder: A DocumentHandler derived from HandlerBase that prints TITLEs


#####


ist ein Attribut mit dem Wert "1"). Da Listing 1 keine Attribute enthält, werden diese nicht in Tabelle 1 angezeigt. Beispiele für Attribute finden Sie in der Beispielanwendung weiter unten in diesem Artikel.