XML-Verarbeitung mit SAX Holger Jakobs – [email protected], [email protected] 2007-06-09 aktuelle Version: http://www.bg.bib.de/portale/xml/pdf/XML-SAX.pdf Inhaltsverzeichnis 1 Einführung in SAX 1.1 Lizenzierung von SAX . . . . . . 1.2 Technische Voraussetzungen . . . 1.3 Kurzbeispiel . . . . . . . . . . . . 1.3.1 Dokument und Ereignisse 1.3.2 Java-Code . . . . . . . . . . . . . . 1 2 2 2 2 3 2 Datenkonvertierung mit SAX 2.1 XML in CSV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 CSV in XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 10 3 Validieren mit Java 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Es gibt zwei Möglichkeiten, ein XML-Dokument zu verarbeiten: Entweder wird das gesamte Dokument in die Anwendung eingelesen und ein passender Baum im Speicher erstellt, oder das Dokument wird elementweise eingelesen und ebenso elementweise verarbeitet. Bei DOM1 wird das gesamte Dokument eingelesen und kann im Speicher beliebig oft gelesen und verändert werden, bevor es dann wieder – in welche Form auch immer – ausgegeben wird. Auch die Erzeugung völlig neuer Dokumente ist möglich. Nachteil ist der Speicherbedarf gerade beim Lesen sehr großer Dokumente. Bei SAX dagegen wird das Dokument nach und nach eingelesen, währenddessen bei jedem gelesenen Element eine von Ihnen geschriebene Methode (callback) aufgerufen wird, die Ihnen mitteilt, dass die Daten nun zur Verarbeitung bereit stehen. Sie können sie ignorieren, in eigene Datenstrukturen einbauen oder gleich wieder ausgeben – je nach Erfordernis. Das bedeutet, dass nur die gerade verarbeiteten Daten überhaupt im Speicher vorgehalten werden. Auf diese Weise können beliebig große Dokumente verarbeitet werden. 1) siehe http://www.bg.bib.de/portale/xml/pdf/XML-DOM.pdf 1 1 EINFÜHRUNG IN SAX 1 Einführung in SAX SAX2 ist die Abkürzung für Simple API for XML“, ein ursprünglich lediglich für Java ” vorgesehenes Interface für die Verarbeitung von XML-Daten. Mittlerweile unterstützen viele Programmiersprachen SAX. SAX verwendet keinen komplett in den Arbeitsspeicher eingelesenen Baum des zu verarbeitenden Dokuments, sondern geht elementweise vor und löst bei jedem Element ein Ereignis aus. Durch diese Ereignissteuerung werden die Inhalte des Dokuments nacheinander mittels Callbacks verarbeitet. Dies minimiert den Arbeitsspeicherbedarf und ermöglicht die Verarbeitung auch sehr großer Dokumente. Mit Hilfe der Callback-Handler kann durchaus während der Verarbeitung eine anwendungsspezifische Struktur des Dokuments im Arbeitsspeicher erstellt werden, aber es wird eben kein generischer Baum der einzelnen Knoten aufgebaut, der das gesamte Dokument repräsentiert. Tatsächlich verwenden viele DOM-Bibliotheken intern einen SAX-Parser, wodurch sogar SAX-spezifische Fehlermeldungen bei der Verwendung von DOM auftreten können. 1.1 Lizenzierung von SAX SAX ist public domain“, es unterliegt keiner Lizenz, auch nicht der GPL oder LGPL. ” Der Name ist nicht geschützt. Innerhalb von Java definiert SAX sein Interface und seinen Code, außerhalb von Java, d. h. bei Verwendung anderer Programmiersprachen, kann SAX abweichend definiert sein. Insbesondere Microsoft hat teilweise andere Vorstellungen davon, was SAX tun sollte und hat es daher anders implementiert. Wegen dieser Schwierigkeiten konzentrieren wir uns auf das Original von Java. 1.2 Technische Voraussetzungen Um mit SAX arbeiten zu können, benötigt man eine möglichst aktuelle Version von Java, mindestens aber Java 1.1, einen SAX-kompatiblen Parser und eine SAX2-Distribution im Java-Classpath. Dies alles ist meistens bereits installiert, wenn man eine Java-Entwicklungsumgebung hat. Man kann im Quellcode statt XMLReaderFactory.createXMLReader() auch new org. apache.xerces.parsers.SAXParser() verwenden, allerdings muss dann auf allen Plattformen, auf denen das Programm laufen soll, genau dieser SAX2-Treiber vorhanden sein. Die Verwendung von createXMLReader() lässt die Verknüpfung mit einem speziellen Treiber offen, so dass sie erst zur Laufzeit durchgeführt wird. Dies ist mit JDBC vergleichbar, wo man einen Treiber im Programm-Quelltext fest verankern kann, oder aber ihn erst zur Laufzeit auswählt. 2) http://sax.sourceforge.net 2 1 EINFÜHRUNG IN SAX 1.3 Kurzbeispiel 1.3 Kurzbeispiel 1.3.1 Dokument und Ereignisse Wir gehen von diesem kleinen XML-Dokument aus: <?xml version="1.0"?> <doc> <para>Hello, world!</para> </doc> Ein ereignisgesteuertes Interface wird dieses Dokument lesen und folgende Ereignisse auslösen: start document start element: doc start element: para characters: Hello, world! end element: para end element: doc end document Die Anwendung behandelt diese Ereignisse prinzipiell genauso wie Ereignisse einer grafischen Oberfläche. Es ist nicht notwendig, das gesamte Dokument im Arbeitsspeicher vorzuhalten. 1.3.2 Java-Code Der Java-Code MySAXApp.java zur Verarbeitung des Dokuments und Anzeige der Ereignisse sieht so aus: import import import import import import import java.io.FileInputStream; java.io.InputStreamReader; org.xml.sax.XMLReader; org.xml.sax.InputSource; org.xml.sax.Attributes; org.xml.sax.helpers.XMLReaderFactory; org.xml.sax.helpers.DefaultHandler; public class MySAXApp extends DefaultHandler { public static void main (String args[]) throws Exception { XMLReader xr = XMLReaderFactory.createXMLReader(); 3 1.3 Kurzbeispiel 1 EINFÜHRUNG IN SAX MySAXApp handler = new MySAXApp(); xr.setContentHandler(handler); xr.setErrorHandler(handler); // Eingabedateien verarbeiten for (int i=0; i < args.length; i++) { FileInputStream fis = new FileInputStream (args [i]); InputStreamReader isr = new InputStreamReader (fis, "UTF-8"); xr.parse (new InputSource (isr)); } // for } // main() public void startDocument () { System.out.println("Start document"); } // startDocument() public void endDocument () { System.out.println("End document"); } // endDocument() public void startElement (String uri, String name, String qName, Attributes atts) { if ("".equals (uri)) System.out.println("Start element: " + qName); else System.out.println("Start element: {" + uri + "}" + name); for (int i=0; i < atts.getLength(); i++) { System.out.println("Attribut: " + atts.getLocalName(i) + " (" + atts.getType(i) + ") = " + atts.getValue(i)); } // for } // startElement() public void endElement (String uri, String name, String qName) { if ("".equals (uri)) System.out.println("End element: " + qName); else System.out.println("End element: {" + uri + "}" + name); } // endElement() public void characters (char ch[], int start, int length) { String ausgabe = ""; 4 1 EINFÜHRUNG IN SAX 1.3 Kurzbeispiel for (int i = start; i < start + length; i++) { if (Character.isWhitespace(ch[i]) && ch[i] != ’ ’) continue; switch (ch[i]) { case ’\\’: ausgabe += "\\\\"; break; case ’"’: ausgabe += "\\\""; break; case ’\n’: ausgabe += "\\n"; break; case ’\r’: ausgabe += "\\r"; break; case ’\t’: ausgabe += "\\t"; break; default: ausgabe += ch[i]; break; } // switch } // for if (ausgabe.length() > 0) { System.out.print("Characters: "); System.out.println(ausgabe); } // if } // characters() } // class MySAXApp Der Name der zu verarbeitenden Datei wird als Parameter übergeben. Es können sogar mehrere Dateinamen übergeben werden, weil eine Schleife über alle Argumente codiert wurde. Durch das Überschreiben der Methoden startDocument(), endDocument(), startElement(), endElement() und characters() werden diese neuen Methoden als Callback bei Eintreten der zugehörigen Ereignisse aufgerufen. Außer bei startDocument() und endDocument() wird über Parameter Information über das aktuell verarbeitete Element bereitgestellt. Aus diesen können die gewünschten Teile entnommen und verarbeitet werden – in diesem einführenden Beispiel werden sie einfach auf der Standardausgabe ausgegeben. 5 2 DATENKONVERTIERUNG MIT SAX 2 Datenkonvertierung mit SAX Im folgenden sollen einige Beispiele zur Datenkonvertierung mit Hilfe von SAX dargestellt werden. Viele Anwendungen benötigen Daten in bestimmten Formaten, die man aus XMLDaten leicht generieren kann. Diese Schnittstellenproblematik ist geradezu allgegenwärtig. Die Konvertierung kann in beide Richtungen notwendig sein – je nach Aufgabenstellung. 2.1 XML in CSV CSV (comma-separated values) ist eines der gängigsten klassischen Datenformate zum Austausch und kann von vielen Programmen im- und exportiert werden, z. B. auch von Tabellenkalkulationen und Textverarbeitungen. Daher bietet sich die Konvertierung von und nach XML geradezu an. Zunächst die Richtung XML in CSV, denn dazu benötigen wir die Verarbeitung von XML mit Hilfe unseres Parsers. Vielleicht werden gar nicht alle Daten benötigt, sondern nur manche. Je nach Art des gefundenen Elements generieren wir eine CSV-Ausgabe oder eben auch nicht. Die zu verarbeitende Datei telefon.xml sieht so aus: <?xml version="1.0"?> <telefonabrechnung> <teilnehmer> <name>Fritz Meier</name> <rufnummer>1234567</rufnummer> <anschrift> <strasse>Hauptstr. 2</strasse> <plz>51465></plz> <ort>Bergisch Gladbach</ort> </anschrift> <bankverbindung> <bank>Postbank Köln</bank> <blz>370 100 50</blz> <konto>123456789</konto> </bankverbindung> </teilnehmer> <zeitraum monat="05" jahr="2004"> <grundpreis>24.00</grundpreis> <ortsgespraeche>12.54</ortsgespraeche> <ferngespraeche>42.44</ferngespraeche> <auslandsgespraeche>0.00</auslandsgespraeche> <sonderrufnummern>0.00</sonderrufnummern> </zeitraum> <zeitraum monat="06" jahr="2004"> <grundpreis>24.00</grundpreis> 6 2 DATENKONVERTIERUNG MIT SAX 2.1 XML in CSV <ortsgespraeche>9.57</ortsgespraeche> <ferngespraeche>52.12</ferngespraeche> <auslandsgespraeche>3.44</auslandsgespraeche> <sonderrufnummern>0.48</sonderrufnummern> </zeitraum> </telefonabrechnung> Frage ist, wie sich diese Daten sinnvoll in eine CSV-Datei konvertieren lassen. In ihrer Gänze ist das sicherlich kaum möglich, weil sie verschiedene Aspekte enthalten, nämlich Angaben zum Kunden und Angaben zu Rechnungen für diverse Zeiträume. Bei der Konvertierung beschränken wir uns daher darauf, nur einen der Aspekte zu berücksichtigen – es sollen hier die Rechnungsdaten extrahiert und umgewandelt werden. Aber auch nach dieser Einschränkung bleiben noch Fragen offen: Soll pro Monat ein Datensatz mit mehreren Spalten erzeugt werden – für jede Gesprächsart eine? Oder sollen die Gesprächsarten untereinander aufgelistet werden, so dass Monat und Jahr jeweils daneben wiederholt werden? Oder sollen gar schon Werte summiert werden und nur als Summe erscheinen? Natürlich gibt es noch weitere Darstellungsmöglichkeiten. Wir können ja mal diese beiden beschriebenen Varianten realisieren. So soll es also aussehen, wenn man es mal tabellarisch darstellt: monat jahr grundortsfernauslandssonderpreis gespraeche gespraeche gespraeche rufnummern 05 2004 24.00 12.54 42.44 0.00 0.00 06 2004 24.00 9.57 52.12 3.44 0.48 Die zweite Variante sähe so aus: monat 05 05 05 05 05 06 06 06 06 06 jahr posten betrag 2004 grundpreis 24.00 2004 ortsgespraeche 12.54 2004 ferngespraeche 42.44 2004 auslandsgespraeche 0.00 2004 sonderrufnummern 0.00 2004 grundpreis 24.00 2004 ortsgespraeche 9.57 2004 ferngespraeche 52.12 2004 auslandsgespraeche 3.44 2004 sonderrufnummern 0.48 Bei der Verarbeitung sollen also nur die zeitraum-Elemente mit all ihren Kind-Elementen berücksichtigt werden. Das Element teilnehmer wird ignoriert, mitsamt all seinen KindElementen. Genau dies muss im Java-Code umgesetzt werden. Dazu setzen wir ein Member der Klasse namens aktiv auf false und schalten es für die Dauer der Verarbeitung von 7 2.1 XML in CSV 2 DATENKONVERTIERUNG MIT SAX zeitraum-Elementen auf true. Eine weitere Verarbeitung findet nur dann statt, wenn der Schalter wahr ist. Das erste Java-Programm telefon_csv1.java erzeugt eine Ausgabe wie in der oberen Tabelle – im Beispiel auf der Standardausgabe. import import import import java.util.HashMap; java.io.FileReader; org.xml.sax.*; org.xml.sax.helpers.*; public class telefon_csv1 extends DefaultHandler { boolean aktiv=false; String monat, jahr; HashMap kosten = new HashMap(); String currentElement; public static void main (String args[]) throws Exception { // XMLReader xr = new org.apache.crimson.parser.XMLReaderImpl(); XMLReader xr = new org.apache.xerces.parsers.SAXParser(); telefon_csv1 handler = new telefon_csv1(); xr.setContentHandler(handler); xr.setErrorHandler(handler); // Eingabedateien verarbeiten for (int i=0; i < args.length; i++) { FileReader r = new FileReader (args [i]); // nur US-ASCII!! System.out.println ("Monat,Jahr,Grund,Ort,Fern,Ausland,Sonder"); xr.parse (new InputSource (r)); } // for } // main() public void startElement (String uri, String name, String qName, Attributes atts) { if (qName.equals ("zeitraum")) { aktiv=true; monat = atts.getValue("monat"); jahr = atts.getValue("jahr"); kosten.clear(); } else { if (!aktiv) return; } // if 8 2 DATENKONVERTIERUNG MIT SAX 2.1 XML in CSV currentElement = name; } // startElement() public void endElement (String uri, String name, String qName) { if (qName.equals ("zeitraum")) { if (kosten.isEmpty()) { System.out.println ("keine Daten vorhanden!"); } else { System.out.print (monat + ","); System.out.print (jahr + ","); System.out.print (kosten.get ("grundpreis") + ","); System.out.print (kosten.get ("ortsgespraeche") + ","); System.out.print (kosten.get ("ferngespraeche") + ","); System.out.print (kosten.get ("auslandsgespraeche") + ","); System.out.println (kosten.get ("sonderrufnummern")); } // if aktiv=false; } // if } // endElement() public void characters (char ch[], int start, int length) { String s = new String (ch).substring (start, start+length); if (s.trim().length() == 0) return; if (!aktiv) return; kosten.put (currentElement, Double.valueOf (s)); // Mit Hilfe von Schema-Validation kann sichergestellt werden, // dass hier keine Format-Exception auftreten wird. } // characters() } // class telefon_csv1 Das zweite Java-Programm telefon_csv2.java unterscheidet sich nur in der Methode endElement und in der Kopfzeilen-Ausgabe, weshalb auch nur diese Methode gezeigt wird: public void endElement (String uri, String name, String qName) { if (qName.equals ("zeitraum")) { if (kosten.isEmpty()) { System.out.println ("keine Daten vorhanden!"); } else { System.out.println (monat + "," + jahr + ",grundpreis," 9 2.2 CSV in XML 2 DATENKONVERTIERUNG MIT SAX + kosten.get ("grundpreis")); System.out.println (monat + "," + jahr + + kosten.get ("ortsgespraeche")); System.out.println (monat + "," + jahr + + kosten.get ("ferngespraeche")); System.out.println (monat + "," + jahr + + kosten.get ("auslandsgespraeche")); System.out.println (monat + "," + jahr + + kosten.get ("sonderrufnummern")); } // if aktiv=false; } // if } // endElement() ",ortsgespraeche," ",ferngespraeche," ",auslandsgespraeche," ",sonderrufnummern," Tatsächlich kann man die zweite Variante sogar weiter im Sinne von XML flexibilisieren, wenn man die Namen der einzelnen Posten gar nicht mehr fest codiert, sondern genau die Posten ausgibt, die in der XML-Datei vorkommen. Wenn es dann neuartige Posten gibt, braucht man das Programm nicht mehr zu verändern. Allerdings ist die Reihenfolge der einzelnen Posten eines Monats nicht mehr steuerbar, nur alphabetisch sortieren könnte man noch. Codiert sieht das so aus: public void endElement (String uri, String name, String qName) { if (qName.equals ("zeitraum")) { if (kosten.isEmpty()) { System.out.println ("keine Daten vorhanden!"); } else { Iterator i = kosten.keySet().iterator(); while (i.hasNext()) { Object key = i.next(); System.out.println (monat + "," + jahr + "," + key + ", " + kosten.get (key)); } // while } // if aktiv=false; } // if } // endElement() Damit es funktioniert, muss allerdings noch java.util.Iterator importiert werden. 2.2 CSV in XML Nun zur umgekehrten Richtung CSV in XML, wozu man keinen XML-Parser benötigt, sondern die Daten einfach mit Java einliest und dann XML generiert. Dies ist allgemein 10 3 VALIDIEREN MIT JAVA der einfachere Vorgang als umgekehrt, denn das CSV-Format ist von seiner Natur her einfacher. Damit es nicht allzu trivial wird, lesen wir zwei CSV-Dateien ein, aus denen dann aber nur eine einzige XML-Datei erzeugt werden soll. Die Daten eines Kunden liegen in der Datei teilnehmer.txt, die Abrechnungsdaten in gebuehren.csv. So haben wir alle Daten beisammen, um die im Abschnitt 2.1 auf Seite 5 gezeigte Datei telefon.xml erzeugen zu können. Es wird also zunächst die Datei mit den Kundendaten geöffnet und gelesen, so dass das Element <teilnehmer> geschrieben werden kann. Die Datei ist folgendermaßen aufgebaut: Rufnummer 1234567 Fritz Meier Hauptstr. 2 51465 Bergisch Gladbach Postbank Köln, BLZ 370 100 50, Konto 123456789 Anschließend wird die Datei gebuehren.csv eingelesen und verarbeitet, wobei aus jeder Zeile ein Element <zeitraum> erzeugt wird: 2004,05,24.00,12.54,42.44,0.00,0.00 2004,06,24.00,9.57,52.12,3.44,0.48 Da dies im Grunde ganz gewöhnliche Dateiverarbeitung mit Java ist, wird hier kein fertiges Programm gezeigt. Zur Probe sollte die erzeugte Datei anschließend mit den Programmen zur Umwandlung von XML in CSV wieder verarbeitet werden. Ein Prüfen mittels SchemaValidator bietet sich ebenfalls an. Alternativ wäre natürlich möglich, mittels DOM3 einen Baum zu erstellen und diesen anschließend als XML auszugeben. Damit wäre zumindest die Wohlgeformtheit in jedem Fall sichergestellt. Für größere Aufgaben – insbesondere wenn Flexibilität gefordert ist – ist das auch der bessere Weg. 3 Validieren mit Java Der SAX-Parser kann auch validieren, hier am Beispiel mit DTD gezeigt. Dies muss von ihm allerdings erst gefordert werden, was durch das Einschalten des entsprechenden Features und Überschreiben der zugehörigen Methoden geschieht. Das kann dann beispielsweise so aussehen: import import import import java.io.FileReader; java.io.FileInputStream; java.io.InputStreamReader; org.xml.sax.SAXNotRecognizedException; 3) siehe http://www.bg.bib.de/portale/xml/pdf/XML-DOM.pdf 11 3 VALIDIEREN MIT JAVA import import import import import import import import import org.xml.sax.SAXNotSupportedException; org.xml.sax.XMLReader; org.xml.sax.InputSource; org.xml.sax.helpers.XMLReaderFactory; org.xml.sax.Attributes; org.xml.sax.helpers.DefaultHandler; org.xml.sax.ErrorHandler; org.xml.sax.SAXException; org.xml.sax.SAXParseException; public class SaxValidate extends DefaultHandler implements ErrorHandler { public static void main (String args[]) throws Exception { XMLReader xr = new org.apache.xerces.parsers.SAXParser(); SaxValidate handler = new SaxValidate(); xr.setContentHandler(handler); xr.setErrorHandler(handler); //xr.setProperty ("http://xml.org/sax/properties/lexical-handler", null); xr.setFeature ("http://xml.org/sax/features/validation", true); try { String id = "http://xml.org/sax/features/validation"; if (xr.getFeature(id)) { System.out.println("Parser is validating."); } else { System.out.println("Parser is not validating."); } // if } catch (SAXNotRecognizedException e) { System.out.println("Can’t tell."); } catch (SAXNotSupportedException e) { System.out.println("Wrong time to ask."); } // try-catch // Eingabedateien verarbeiten for (int i=0; i < args.length; i++) { //FileReader r = new FileReader (args [i]); // Annahme: (US-)ASCII-Datei FileInputStream fis = new FileInputStream (args [i]); InputStreamReader isr = new InputStreamReader (fis, "UTF-8"); xr.parse (new InputSource (isr)); 12 3 VALIDIEREN MIT JAVA } // for } // main() public void startDocument () { System.out.println("Start document"); } // startDocument() public void endDocument () { System.out.println("End document"); } // endDocument() public void startElement (String uri, String name, String qName, Attributes atts) { if ("".equals (uri)) System.out.println("Start element: " + qName); else System.out.println("Start element: {" + uri + "}" + name); for (int i=0; i < atts.getLength(); i++) { System.out.println("Attribut: " + atts.getLocalName(i) + " (" + atts.getType(i) + ") = " + atts.getValue(i)); } // for } // startElement() public void endElement (String uri, String name, String qName) { if ("".equals (uri)) System.out.println("End element: " + qName); else System.out.println("End element: {" + uri + "}" + name); } // endElement() public void characters (char ch[], int start, int length) { String ausgabe = ""; for (int i = start; i < start + length; i++) { if (Character.isWhitespace(ch[i]) && ch[i] != ’ ’) continue; switch (ch[i]) { case ’\\’: ausgabe += "\\\\"; break; case ’"’: ausgabe += "\\\""; break; 13 3 VALIDIEREN MIT JAVA case ’\n’: ausgabe += "\\n"; break; case ’\r’: ausgabe += "\\r"; break; case ’\t’: ausgabe += "\\t"; break; default: ausgabe += ch[i]; break; } // switch } // for if (ausgabe.length() > 0) { System.out.print("Characters: System.out.println(ausgabe); } // if } // characters() "); /********************************************************************* Folgende Variablen und Methoden dienen zur Fehlerbehandlung beim Validieren **********************************************************************/ boolean boolean boolean boolean reportErrors = abortOnError = reportWarnings abortOnWarning true; true; = true; = false; private void print (String label, SAXParseException e) { System.err.println ("** " + label + ": " + e.getMessage ()); System.err.println (" URI = " + e.getSystemId ()); System.err.println (" line = " + e.getLineNumber ()); } // print() // für fatale Fehler public void fatalError (SAXParseException fe) throws SAXException { if (reportErrors) print ("fatal error", fe); if (abortOnError) throw fe; } // fatalError() 14 3 VALIDIEREN MIT JAVA // für nicht-fatale Fehler, z. B. Validitätsprobleme public void error (SAXParseException e) throws SAXException { if (reportErrors) print ("error", e); if (abortOnError) throw e; } // error() // für Warnungen public void warning (SAXParseException w) throws SAXException { if (reportErrors) print ("warning", w); if (abortOnWarning) throw w; } // warning() } // class SaxValidate Die XML-Datei wird mittels FileInputStream fis = new FileInputStream (args [i]); InputStreamReader isr = new InputStreamReader (fis, "UTF-8"); xr.parse (new InputSource (isr)); geöffnet und verarbeitet, weil das Öffnen in den anderen Programmen die Dateien automatisch aus US-ASCII-Dateien verarbeitet. Da in den XML-Daten aber angegeben ist, dass es sich um UTF-8 handelt, muss das auch so gelesen werden, um eine entsprechende Warnung zu verhindern. So lange die Daten nur 7-Bit-Daten sind, ist das natürlich kein Problem, sobald aber Umlaute oder andere Zeichen jenseits des US-ASCII-Zeichensatzes dabei sind, dann würden die Daten tatsächlich falsch interpretiert, wenn die Datei in UTF -8 codiert ist, die Standard-Codierung der Prozessumgebung aber eine andere ist (bei Windows XP beispielsweise Windows-Codepage-1252, bei Linux-Distributionen ist sie mittlerweile quasi komplett UTF-8). Eine Beispieldatei, die einen Fehler meldet – wegen des falschen Elements <wurst>“, ” wäre diese (wetter2.xml): <?xml version="1.0" encoding="UTF8"?> <!DOCTYPE weather-report SYSTEM "http://www.bg.bib.de/~bibjah/wreport.dtd"> <?xml-stylesheet type="text/xsl" href="wreport3-style.xml" ?> <weather-report> <date>März 06, 2000</date> <time>09:10</time> <area> 15 3 VALIDIEREN MIT JAVA <city>Cologne</city> <region >NRW</region> <country>Germany</country> <wurst>Hans</wurst> </area> <measurements> <skies>fine</skies> <temperature scale="C">15</temperature> <wind> <direction>SW</direction> <windspeed>6</windspeed> </wind> </measurements> </weather-report> Im obigen Programm ist der Aufruf des Crimson-Parsers auskommentiert und durch einen Aufruf des neueren Xerces-Parsers (ebenfalls aus dem Apache-Projekt) ersetzt worden. $Id: XML-SAX.tex,v 1.1 2007-05-30 10:46:35 hj Exp $ 16