Advanced   Java   Services XMLReader und XMLReaderFactory Back Next Up Home


Factory

SAX (Simple Access for XML) kann XML-Dateien nur Lesen, dies aber sehr performant. SAX arbeitet dabei mit Events. Immer wenn der Parser auf ein Tag stößt gibt es einen Event und es wird eine entprechende Methode aufgerufen. Diese Methoden sind im Interface org.xml.sax.ContentHandler vereinbart. Man implementiert also einen ContentHandler und gibt ihn dem Parser bekannt. Dazu muß bei diesem Parser eine Zwischeninstanz vom Typ org.xml.sax.InputSource angelegt werden, da der Parser keine Methode besitzt, der man einen InputStream oder einen Reader direkt übergeben kann.

Die Erzeugung der notwendigen Instanzen wird dabei immer von einer Factoryklasse angestossen.


Codeschema main

Die Factory erzeugt ein Objekt vom Typ com.sun.org.apache.xerces.internal.parsers.SAXParser. Der ContentHandler muß selbst geschrieben werden. Dazu gibt es die Hilfsklasse org.xml.sax.helpers.DefaultHandler. Sie ist die Basisklasse für alle SAX2 Event Handler und implementiert die Methoden der folgenden Interfaces in Form von leeren Methoden.

Wir wollen unserem DefaultHandler auch als ErrorHandler benutzen und übergeben ihn daher auch der Methode setErrorHandler(). Einen fatalen Fehler kann man etwa dadurch provozieren, indem man in die XML-Datei einen Syntaxfehler einbaut.

try
{
   XMLReader xmlReader = XMLReaderFactory.createXMLReader()  ;
   DefaultHandler myHandler = new MyDefaultHandler();
   xmlReader.setContentHandler(myHandler);
   xmlReader.setErrorHandler(myHandler);

   Reader br = new BufferedReader( new FileReader("buecher.xml") );
   InputSource inputSource = new InputSource(br);
   xmlReader.parse(inputSource);
}
catch (SAXException | IOException ex)
{
   ex.printStackTrace();
}

Die Beispieldatei

Die Xml-Datei, die der ContentHandler einlesen soll.

<?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>123456789</isbn>
   </buch>

   <buch type="paperback">
      <titel>Der Untertan</titel>
      <autor>
         <vorname>Heinrich</vorname>
         <nachname>Mann</nachname>
      </autor>
      <isbn>234567891</isbn>
   </buch>

   <buch type="hardcover">
      <titel>Erfolg</titel>
      <autor>
         <vorname>Lion</vorname>
         <nachname>Feuchtwanger</nachname>
      </autor>
      <isbn>345678912</isbn>
   </buch>

</buecher>

Der leere DefaultHandler

Es ist sinnvoll, auch die Methoden aus dem Interface ErrorHandler zu realisieren.

public class MyDefaultHandler1 extends DefaultHandler
{
   // Receive notification of the start of an element.
   @Override
   public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
   {}

   // Receive notification of the end of an element.
   @Override
   public void endElement(String uri, String localName, String qName) throws SAXException
   {}

   // Receive notification of character data inside an element.
   @Override
   public void characters(char[] ch, int start, int length)
   {}

   //ErrorHandler

   //Receive notification of a warning.
   @Override
   public void warning(SAXParseException ex) throws SAXException
   {}

   //Receive notification of a recoverable error.
   @Override
   public void error(SAXParseException ex) throws SAXException
   {}

   //Receive notification of a non-recoverable error.
   @Override
   public void fatalError(SAXParseException ex) throws SAXException
   {}
}

Über die Methode startElement() kann man das Tag feststellen, auf das der Parser gestoßen ist. In der Methode characters() kann man dann die zugehörigen Daten abholen. Die Methode endElement() wird aufgerufen, wenn das Endetag gelesen wurde. Mit den aus dem Interface ErrorHandler stammenden Methoden kann man Warning- und Fehlermeldungen mitgeteilt bekommen


Vorsicht bei characters(char[] ch, int start, int length)

Die Spezifikation schreibt nicht vor, daß diese Methode die anliegenden Daten auf einmal einliest. Die Daten zu einem Tag können also u.U. (auch aus Grunden der Performanc) häppchenweise geliefert werden. Am Besten legt man also in startElement() einen "Datensammler" an, den man ihn characters() füllt.


Der fertige DefaultHandler

public class MyDefaultHandler extends DefaultHandler
{
   private boolean booTitel = false;
   private boolean booVorname = false;
   private boolean booNachname = false;
   private boolean booISBN = false;
   private StringBuilder sb = null;

   // Receive notification of the start of an element.
   @Override
   public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
   {
      if(qName.equals("buch"))
      {
         System.out.print("Buch");
         // attributes.getLength() liefert die Anzahl der Attribute
         // getLocalName(i)) liefert den key
         String value = attributes.getValue(0); // liefert den value, hier also paperback oder hardcover
         System.out.println( " (" + value + ")");
      }
      if(qName.equals("titel"))
      {
         booTitel = true;
         System.out.print("Titel: ");
         sb = new StringBuilder();
      }
      if(qName.equals("autor"))
      {
         System.out.print("Autor: ");
      }
      if(qName.equals("vorname"))
      {
         sb = new StringBuilder();
         booVorname = true;
      }
      if(qName.equals("nachname"))
      {
         sb = new StringBuilder();
         booNachname = true;
      }
      if(qName.equals("isbn"))
      {
         sb = new StringBuilder();
         System.out.print("ISBN: ");
         booISBN = true;
      }
   }

   // Receive notification of the end of an element.
   @Override
   public void endElement(String uri, String localName, String qName) throws SAXException
   {
      if(qName.equals("titel"))
      {
         booTitel = false;
         System.out.println(sb);
         sb=null;
      }
      if(qName.equals("vorname"))
      {
         booVorname = false;
         System.out.print(sb + " ");
         sb=null;
      }
      if(qName.equals("nachname"))
      {
         booNachname = false;
         System.out.println(sb);
         sb=null;
      }
      if(qName.equals("isbn"))
      {
         booISBN = false;
         System.out.println(sb);
         sb=null;
      }
   }

   // Receive notification of character data inside an element.
   @Override
   public void characters(char[] ch, int start, int length)
   {
      if(booTitel)     sb.append(new String(ch, start, length).trim());
      if(booVorname)   sb.append(new String(ch, start, length).trim());
      if(booNachname)  sb.append(new String(ch, start, length).trim());
      else if(booISBN) sb.append(new String(ch, start, length).trim());
   }

   @Override
   //Receive notification of a warning.
   public void warning(SAXParseException ex) throws SAXException
   {
      System.out.println(ex);
   }

   @Override
   //Receive notification of a recoverable error.
   public void error(SAXParseException ex) throws SAXException
   {
      System.out.println(ex);
   }

   @Override
   //Receive notification of a non-recoverable error.
   public void fatalError(SAXParseException ex) throws SAXException
   {
      System.out.println(ex);
   }
}

Programmoutput
com.sun.org.apache.xerces.internal.parsers.SAXParser@691177
Buch (paperback)
Titel: Deutschland, ein Wintermaerchen
Autor: Heinrich Heine
ISBN: 123456789
Buch (paperback)
Titel: Der Untertan
Autor: Heinrich Mann
ISBN: 234567891
Buch (hardcover)
Titel: Erfolg
Autor: Lion Feuchtwanger
ISBN: 345678912

Valid XHTML 1.0 Strict top Back Next Up Home