Parser-Sax Grundlagen

Genug der öden Theorie, widmen wir uns der Praxis. Ich denke, am besten ist es, wir stürzen uns gleich auf das erste Beispiel:

import com.ibm.xml.parsers.*;
import org.xml.sax.*;


public class test
{
    public static void main(String[] args)
    {
        test myTest = new test();
        myTest.parseIt(args[0]);
    }


    public void parseIt(String file)
    {
        SAXParser parser = new SAXParser();
        try
        {
            parser.parse(file);
        }
        catch (Exception e)
        {
            System.err.println("Fehler beim parsen: ");
            System.err.println(e);
        }
    }
}

Der Gedanke bei diesem Miniprogramm war, daß man ihm den Dateinamen einer XML-Datei übergibt. Diese soll dann geparst werden.

Was passiert nun in diesem Beispiel? Zuersteinmal verwende ich den IBM-Parser. Deshalb muß das entsprechende Paket importiert werden. Außerdem benötigen wir natürlich die SAX-Klassen. Diese stammen nicht von IBM, sondern sind standardisiert.

Die nächste interessante Zeile ist dann

SAXParser parser = new SAXParser();

Hier wird der nichtvalidierende SAX-Parser instantiiert. Der Parser besitzt eine Methode namens parse. Mittels dieser Methode kann man - welch Wunder - ein XML-Dokument parsen. Dazu muß man ihr nur den Dateinamen des XML-Files übergeben.

Wie würde das ganze nun mit dem Sun-Parser aussehen?

import com.sun.xml.parser.Parser;
import com.sun.xml.parser.Resolver;
import org.xml.sax.*;


public class test
{
    public static void main(String[] args)
    {
        test myTest = new test();
        myTest.parseIt(args[0]);
    }


    public void parseIt(String file)
    {
        Parser parser = new Parser();
        try
        {
            parser.parse(Resolver.createInputSource(new java.io.File(file)));
        }
        catch (Exception e)
        {
            System.err.println("Fehler beim parsen: ");
            System.err.println(e);
        }
    }
}

Mal abgesehen davon, daß der nichtvalidierende SAX-Parser bei Sun Parser heißt, und nicht wie bei IBM SAXPaser, ist leider die parse-Methode bei Sun etwas komplizierter zu benutzen. Ihr müßt ihr nämlich an Stelle des Dateinamens eine InputSource übergeben. Diese wird mit Resolver.createInputSource erzeugt. Leider kann man dieser statischen Methode auch nicht einfach einen Dateinamen übergeben, sondern muß diesen erst in ein File-Objekt umwandeln.

Dieses Programm ist so natürlich total sinnlos, da das XML-Dokument zwar geparst wird, die Events, die der Parser erzeugt, aber nicht behandelt werden. Genaugenommen werden sie schon behandelt, aber trivial, das heißt, es passiert garnichts, wie ihr wahrscheinlich gemerkt habt, wenn ihr das Programm ausprobiert habt. Die nächste Aufgabe besteht folglich darin, einen Eventhandler zu schreiben, der etwas mir den Ereignissen anfangen kann.

Um einen Eventhandler zu schreiben, müßt ihr ein entsprechendes Interface implementieren. Ich werde hier nur auf die beiden wichtigsten Eventhandler eingehen, nämlich den Documenhandler und den Errorhandler. Wozu die jeweiligen Handler dienen (falls euch das nicht schon klar ist), werde ich am Beispiel zeigen.

Schreiben wir uns also zunächst einen Dokumenthandler. Dieser soll eigentlich nichts weiter tun, als das ganze XML-Dokument auf dem Bildschirm auszugeben. Ich habe mich übrigens nicht um die Formatierung der Ausgabe bemüht, um das Beispiel nicht zu unübersichtlich werden zu lassen. Daher wird der Output sehr chaotisch aussehen. Seht es als Ansporn, das Beispiel auszubauen.

import org.xml.sax.*;


public class MyDocumentHandler implements DocumentHandler
{
    public void startDocument() 
throws SAXException {
System.out.println("<?xml version=\"1.0\"?>"); } public void endDocument()
throws SAXException
{} public void startElement(String name, AttributeList attrs)
throws SAXException { System.out.print('<'); System.out.print(name); if (attrs != null) { int length = attrs.getLength(); for (int i = 0; i < length; i++) { System.out.print(attrs.getName(i));
System.out.print("=\"");
System.out.print(attrs.getValue(i));
System.out.print("\" ");
}
} System.out.println(">");
} public void endElement(String name)
throws SAXException {
System.out.println("</" + name + ">");
} public void characters(char[] c, int start, int length)
throws SAXException { System.out.print(new String(c, start, length)); } public void ignorableWhitespace(char[] c, int start, int length)
throws SAXException { characters(c, start, length); } public void processingInstruction(String target, String data)
throws SAXException {
System.out.print("<?");
Systemout.print(target); if (data != null && data.length() > 0) System.out.print(" " + data);
System.out.println("?>");
} public void setDocumentLocator(org.xml.sax.Locator arg1) {}
}

Jede Methode im Documenthandler wird bei einem bestimmten Event aufgerufen. Ich denke, welche Methode zu welchem Event gehört, findet ihr selber raus.

Jetzt fehlt eigentlich nurnoch das Parserobjekt, das das XML-Dokument liest:

import com.ibm.xml.parsers.*;

public class MyWriter { public static void main(String[] args) { MyWriter writer = new MyWriter(); writer.parse(args[0]); }

private void parse(String file) { SAXParser parser = new SAXParser(); parser.setDocumentHandler(new MyDocumentHandler()); try { parser.parse(file); } catch (Exception e) { System.err.println("Fehler beim Parsen"); System.err.println(e); } } }

Das sollte euch eigentlich sofort verständlich sein, da die einzige Neuerung, das Einbinden des Documenthandlers mittels parser.setDocumentHandler ist.

Schauen wir uns nun mal an, wie das ganze beim Sun-Parser aussehen würde. Das schöne ist: nicht viel anders. Der Documenthandler bleibt komplett der gleiche, nur die Instantiierung des Parsers muß auf die schon bekannte Art verändert werden:

import com.sun.xml.parser.Parser;
import com.sun.xml.parser.Resolver;

public class MyWriter { public static void main(String[] args) { MyWriter writer = new MyWriter(); writer.parse(args[0]); }

private void parse(String file) { Parser parser = new Parser(); parser.setDocumentHandler(new MyDocumentHandler()); try { parser.parse(Resolver.createInputSource(new java.io.File(file))); } catch (Exception e) { System.err.println("Fehler beim Parsen"); System.err.println(e); } } }

Ihr könnt natürlich auch die Fehlerbehandlung euren Bedürfnissen anpassen. Dazu müßt ihr einen Errorhandler schreiben. Das Prinzip ist dabei das gleiche, wie beim Documenthandler. Ein einfacher Errorhandler könnte etwa so aussehen:

import org.xml.sax.*;

public class MyErrorHandler implements ErrorHandler
{
    public void warning(SAXParseException e)
    throws SAXException
    {
        System.err.println("warning: " + e);
    }

    public void error(SAXParseException e)
    throws SAXException
    {
        System.err.println("error: " + e);
    }

    public void fatalError(SAXParseException e)
    throws SAXException
    {
        System.err.println("fatal error: " + e);
    }
}

Die Funktionsweise bleibt die gleiche: jede Methode wird aufgerufen, wenn das entsprechende Event auftritt. Die Namen der Methoden sprechen für sich selbst, oder?

Beim Parser kann man diesen Errorhandler analog zum Documenthandler registrieren (erstaunlich, gelle?), und zwar mit

parser.setErrorHandler(new MyErrorHandler());

zurück              weiter

nach oben