Advanced Java Services | StAX XMLStreamReader |
Mit java 1.6 wurde eine neue API eingeführt, die ähnlich wie SAX arbeitet, aber zusätzlich auch das Schreiben von XML-Dateien unterstützt, die Streaming API for XML, kurz auch StAX. Eine Instanz von javax.xml.stream.XMLStreamReader wird wieder über eine Factory erzeugt, die javax.xml.stream.XMLInputFactory. Die Factory liefert die Instanz einer Klasse die das Interface XMLStreamReader implementiert hat.
Ein XMLStreamReader arbeitet nach dem sog. Cursorverfahren. Der Cursor ist eine Art Dateipositionszeiger, der vom Entwickler selbst weitergeschoben werden muß. Ähnlich wie bei einem Iterator gibt es deshalb Methoden wie hasNext() und next(), mit denen man von einer "interessanten Stelle" in der Datei zur nächsten springen kann. Interessante Stellen sind dabei XML-Tags und XML-Datensegmente usw. Die StAX-API wird deshalb auch eine "PULL-API" genannt, weil der Entwickler für das Iterieren sorgen muß, es geschieht nicht von selbst wie bei der eventgesteuerten SAX-API. Wenn der Cursor stoppt, so hat er einen neuen Zustand erreicht und man muß herausfinden, warum der Cursor angehalten hat. Der Returnwert der next()-Methode liefert die dazu notwendige Information.
Das Erzeugen des Readers ist einfach. Interessant ist der Puller, der in eine eigene Klasse verlegt wurde.
try { XMLInputFactory factory = XMLInputFactory.newInstance(); // com.sun.xml.internal.stream.XMLInputFactoryImpl XMLStreamReader xMLStreamReader = factory.createXMLStreamReader( new FileReader("buecher.xml") ); // com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl MyXMLStreamReaderPuller handler = new MyXMLStreamReaderPuller(xMLStreamReader); } catch (FileNotFoundException | FactoryConfigurationError | XMLStreamException ex) { ex.printStackTrace(); }
In einer einfachen Fassung des Pullers wird nur festgestellt, an welchen Stellen der Cursor stoppt.
public class MyXMLStreamReaderPuller { XMLStreamReader xmlStreamReader; public MyXMLStreamReaderPuller(XMLStreamReader xMLStreamReader) throws XMLStreamException { this.xmlStreamReader = xMLStreamReader; pull(); } /** * Untersucht welche types überhaupt vorkommen * Es kommen vor: * START_ELEMENT, END_ELEMENT, CHARACTERS, END_DOCUMENT */ private void pull() throws XMLStreamException { while(xmlStreamReader.hasNext()) { int type = xmlStreamReader.next(); switch(type) { case XMLStreamReader.START_ELEMENT: System.out.println("START_ELEMENT"); break; case XMLStreamReader.END_ELEMENT: System.out.println("END_ELEMENT"); break; case XMLStreamReader.ATTRIBUTE: System.out.println("ATTRIBUTE"); break; case XMLStreamReader.CDATA: System.out.println("CDATA"); break; case XMLStreamReader.CHARACTERS: System.out.println("CHARACTERS"); break; case XMLStreamReader.END_DOCUMENT: System.out.println("END_DOCUMENT"); break; default: System.out.println("other"); } } } }
Es kommen hier nur vier verschiedene Zustände vor: START_ELEMENT, END_ELEMENT, CHARACTERS, END_DOCUMENT
Bei ISBN ist ein Attribut ergänzt worden.
<?xml version="1.0" encoding="UTF-8"?> <buecher> <buch type="paperback"> <titel>Deutschland, ein Wintermaerchen</titel> <autor> <vorname>Heinrich</vorname> <nachname>Heine</nachname> </autor> <isbn type="10">3-86666-192-9</isbn> </buch> <buch type="paperback"> <titel>Der Untertan</titel> <autor> <vorname>Heinrich</vorname> <nachname>Mann</nachname> </autor> <isbn type="10">3-86677-192-9</isbn> </buch> <buch type="hardcover"> <titel>Erfolg</titel> <autor> <vorname>Lion</vorname> <nachname>Feuchtwanger</nachname> </autor> <isbn type="10">3-86688-192-9</isbn> </buch> </buecher>
public class MyXMLStreamReaderPuller { XMLStreamReader xmlStreamReader; public MyXMLStreamReaderPuller(XMLStreamReader xMLStreamReader) throws XMLStreamException { this.xmlStreamReader = xMLStreamReader; pull(); } /** * @param xmlStreamReader * @throws XMLStreamException * * getElementText() wirft XMLStreamException * wenn nach dem Starttag nochmal ein Tag kommt statt text * * Attribute müssen VOR dem Text ausgelesen werden, da die Methode getElementText * den Zustand auf END_ELEMENT setzt und im Endetag gibt es ja keine Attribute */ private void pull() throws XMLStreamException { while (xmlStreamReader.hasNext()) { int type = xmlStreamReader.next(); switch (type) { case XMLStreamReader.START_ELEMENT: String startTag = xmlStreamReader.getLocalName(); if (startTag.equals("buch")) { System.out.print("Buch "); // System.out.println(xmlStreamReader.getAttributeName(0)); // liefert den key, hier also type System.out.println("(" + xmlStreamReader.getAttributeValue(0) + "):"); // liefert den value, hier also hardcover etc. } if (startTag.equals("titel")) { System.out.println(xmlStreamReader.getElementText()); } if (startTag.equals("autor")) { System.out.print("Autor: "); } if (startTag.equals("vorname")) { System.out.print(xmlStreamReader.getElementText() + " "); } if (startTag.equals("nachname")) { System.out.println(xmlStreamReader.getElementText()); } if (startTag.equals("isbn")) { //System.out.println("titel AttributeCount " + xmlStreamReader.getAttributeCount()); // 1 String isbntype = xmlStreamReader.getAttributeValue(0); // System.out.println("ISBN-" + isbntype + ": " + xmlStreamReader.getElementText()); } break; } } } }
Das Attribut bei <isbn> wurde eingebaut, um auf eine Stolperstelle hinzuweisen. Die Methode getElementText() ändert nämlich den Zustand des Cursors auf END_ELEMENT. Da es im Endetag keine Attribute gibt, löst der Aufruf einer getAttributeXxx()-Methode eine IllegalStateException aus. Die Lösung ist einfach, man muß die Attributmethoden vor einem Aufruf von getElementText() plazieren.
Buch (paperback): Deutschland, ein Wintermaerchen Autor: Heinrich Heine ISBN-10: 3-86666-192-9 Buch (paperback): Der Untertan Autor: Heinrich Mann ISBN-10: 3-86677-192-9 Buch (hardcover): Erfolg Autor: Lion Feuchtwanger ISBN-10: 3-86688-192-9