Advanced Java Services | XMLReader und XMLReaderFactory |
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.
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 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>
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
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.
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); } }
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