Advanced   Java   Services StAX   XMLStreamReader Back Next Up Home


XMLStreamReader und XMLInputFactory

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.


Stax ist eine "PULL"-API (Cursorverfahren)

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.


Codeschema main

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();
}

Ein TestPuller

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");
         }
      }
   }
}


Die Stoppstellen

Es kommen hier nur vier verschiedene Zustände vor: START_ELEMENT, END_ELEMENT, CHARACTERS, END_DOCUMENT


Die XML-Datei

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>

Der Puller

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;
         }
      }
   }
}

Vorsicht beim Auslesen der Attribute

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.





Der Output
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

Valid XHTML 1.0 Strict top Back Next Up Home