Verarbeitung von XML- Dokumenten
Lernziele Was unterscheidet Pull- von Push-Parser? Was unterscheidet Einschritt- von Mehrschritt-Parser? Wie ordnen sich SAX und DOM bezüglich dieser Kategorien ein? Warum sollte immer die Anwendungslogik vom Datenzugriff getrennt werden? Was sind Schema-Übersetzer?
Grundlegende Architektur Zeichenkette XML- Dokument Serialisierer Parser standardisierte APIs Anwendung Möglichst immer standardisierte APIs verwenden! Parser: Analysiert XML-Dokument und erstellt Parse-Baum mit Tags, Text-Inhalten und Attribut-Wert-Paaren als Knoten. Serialisierer: Generiert aus Datenstruktur einer Anwendung ein XML-Dokument.
Kategorien von Parser Parser können bezüglich folgender Kriterien eingeordnet werden: Pull- vs. Push-Parser: Wer hat die Kontrolle über das Parsen, die Anwendung oder der Parser selbst? Einschritt- vs. Mehrschritt-Parser (engl. one-step vs. multistep parsing): Wird das XML-Dokument in einem Schritt vollständig geparst oder werden die einzelnen syntaktischen Einheiten Schritt für Schritt analysiert? Beachte: Die Kategorien sind unabhängig voneinander, sie können beliebig kombiniert werden.
Pull-Parser Parser Pull-Parser nächste Einheit? übersetzte Einheit Anwendung Die Anwendung hat die Kontrolle über das Parsen. Die Analyse der nächsten syntaktischen Einheit muss von der Anwendung aktiv angefordert werden.
Push-Parser Parser Push-Parser übersetzte Einheit Anwendung Der Parser selbst hat die Kontrolle über das Parsen. Sobald der Parser eine syntaktische Einheit analysiert hat, benachrichtigt er die Anwendung und übergibt die entsprechende Analyse.
XML-Parser One-step Multi-step Pull DOM JAXP Push SAX DOM: Document Object Model SAX: Simple API for XML JAXP: Java API for XML Processing
Simple API for XML (SAX) standardisiertes API für Push-Mehrschritt-Parsen von XML-Dokumenten ursprünglich nur Java-API, inzwischen werden aber auch andere Sprachen unterstützt kein W3C-Standard, sondern de facto Standard
Ereignisbasiertes Parsen SAX-Parser Parse Information Event Handler Anwendung
Ereignisbasiertes Parsen <pricelist> [Parser ruft startelement auf] <coffee> [Parser ruft startelement auf] <name> [Parser ruft startelement auf] Mocha Java Java [Parser ruft characters auf] </name> [Parser ruft endelement auf] <price> [Parser ruft startelement auf] 11.95 [Parser ruft characters auf] </price> [Parser ruft endelement auf] </coffee> [Parser ruft endelement auf] </pricelist> [Parser ruft endelement auf] Sobald eine syntaktische Einheit geparst wurde, wird die Anwendung benachrichtigt: z.b. mit startelement(,pricelist, ) Beachte: Es wird kein Parse-Baum aufgebaut!
Callback-Methoden startelement, endelement und characters werden Call-Back- Methoden genannt, weil sie vom Parser aufgerufen werden. Für jede syntaktische Struktur in XML gibt es eine eigene Callback-Methode. Die Standard-Implementierung der Call-Back-Methoden (DefaultHandler) machen gar nichts. Jede Callback-Methode kann aber entsprechend den Erfordernissen überschrieben werden.
Ein SAX-Parser SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxparser = factory.newsaxparser(); saxparser.parse("pricelist.xml", handler); SAXParserFactory.newInstance() liefert eine SAXParserFactory. Die Methode newsaxparser() liefert einen SAXParser mit einer Methode parse(). pricelist.xml: die zu parsende Datei (kann auch eine URL oder ein Stream sein). handler: Instanz von DefaultHandler, implementiert die Callback-Funktionen.
Exkurs: Factory Method Design Pattern aus Design Patterns von Gamma, Helm, Johnson, Vlissides (1995). liefert ein Objekt Objekt ist Instanz einer abstrakten Klasse oder einem Interface. wird von mehreren Klassen implementiert Beispiel: Iterator i = list.iterator();
Beispiel <pricelist> <coffee> <name> Mocha Java Java </name> <price> 11.95 </price> </coffee> </pricelist> Gib des Preis des Kaffees Mocha Java aus!
Die Callback-Methoden public void voidstartelement(..., String elementname,...){...){ if if (elementname.equals("name")){ inname = true; }} else elseif if (elementname.equals("price") && && inmochajava ){ ){ inprice = true; true; <name>mocha Java</name> inname = false; } } <price>11.95</price> public void voidcharacters(char [] [] buf, buf, int intoffset, int intlen) { String s s = new newstring(buf, offset, len); len); if if (inname && && s.equals("mocha Java")) { inmochajava = true; true; inname = false; } else elseif if (inprice) { System.out.println("The price priceof of Mocha Java Java is: is: "" + s); s); inmochajava = false; inprice = false; } }
Die Callback-Methoden public void voidstartelement(..., String elementname,...){...){ if if (elementname.equals("name")){ inname = true; true; } else elseif if (elementname.equals("price") && && inmochajava ){ ){ inprice = true; true; <name>mocha Java</name> inname = false; } } <price>11.95</price> public void voidcharacters(char [] [] buf, buf, int intoffset, int intlen) { String s s = new newstring(buf, offset, len); len); if if (inname && && s.equals("mocha Java")) {{ inmochajava = true; inname = false; }} else elseif if (inprice) { System.out.println("The price priceof of Mocha Java Java is: is: "" + s); s); inmochajava = false; inprice = false; } }
Die Callback-Methoden public void voidstartelement(..., String elementname,...){...){ if if (elementname.equals("name")){ inname = true; true; } else elseif if (elementname.equals("price") && && inmochajava ){ ){ inprice = true; <name>mocha Java</name> inname = false; }}} <price>11.95</price> public void voidcharacters(char [] [] buf, buf, int intoffset, int intlen) { String s s = new newstring(buf, offset, len); len); if if (inname && && s.equals("mocha Java")) { inmochajava = true; true; inname = false; } else elseif if (inprice) { System.out.println("The price priceof of Mocha Java Java is: is: "" + s); s); inmochajava = false; inprice = false; } }
Die Callback-Methoden public void voidstartelement(..., String elementname,...){...){ if if (elementname.equals("name")){ inname = true; true; } else elseif if (elementname.equals("price") && && inmochajava ){ ){ inprice = true; true; <name>mocha Java</name> inname = false; } } <price>11.95</price> public void voidcharacters(char [] [] buf, buf, int intoffset, int intlen) { String s s = new newstring(buf, offset, len); len); if if (inname && && s.equals("mocha Java")) { inmochajava = true; true; inname = false; } else elseif if (inprice) {{ System.out.println("The price of of Mocha Java Java is: is: " + s); s); inmochajava = false; inprice = false; } }
Vor- und Nachteile von SAX + sehr effizient, auch bei großen XML-Dateien Kontext (Parse-Baum) muss von der Anwendung selbst verwaltet werden. Abstrahiert nicht von der spezifischen XML-Syntax. nur Parsen von XML-Dokumenten möglich, keine Modifikation oder Erstellung von Dokumenten
Weitere Informationen zu SAX http://www.saxproject.org/
Zusätzliche Schicht zum Datenzugriff Anwendung SAX SAX startelement characters Datenzugriff Datenzugriff getpricelist() Anwendungslogik Immer Anwendungslogik durch spezielle Schicht von dem Datenzugriff trennen (nicht nur bei SAX). Z.B. könnte getpricelist() eine Liste von Ware-Preis- Paaren liefern. Sollte nicht nur von SAX-APIs, sondern auch von der XML-Syntax abstrahieren.
Document Object Model (DOM) XML- Parser DOM Anwendung W3C-Standard zum Zugreifen, Modifizieren und Erstellen von Parse-Bäumen nicht nur für XML-, sondern auch für HTML-Dokumente abstrakte Schnittstelle, unabhängig von Programmiersprachen im Ergebnis ein Einschritt-Pull-Parser
Parse-Bäume in DOM <?xml version="1.0"?>?> <pricelist> <coffee> <name>mocha Java</name> <price>11.95</price> </coffee> </pricelist> Document Node NodeList Element Node: PriceList NodeList Element Node: coffee Beachte: In DOM ist pricelist nicht die Dokument-Wurzel. DOM führt virtuelle Dokument-Wurzel ein, um auch syntaktische Einheiten außerhalb von pricelist repräsentieren zu können.
Parse-Bäume in DOM <?xml version="1.0"?>?> <pricelist> <coffee> <name>mocha Java</name> <price>11.95</price> </coffee> </pricelist> Beachte: Text-Inhalte werden als eigene Knoten dargestellt. Element Node: coffee NodeList Element Node: name NodeList Text Node: Mocha Java Element Node: price NodeList Text Node: 11.95
Navigationsmodell parentnode previoussibling Node nextsibling childnodes firstchild lastchild
Ein DOM-Parser DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newdocumentbuilder(); Document document = builder.parse("pricelist.xml"); DocumentBuilderFactory.newInstance() liefert eine DocumentBuilderFactory. Die Methode newdocumentbuilder() von DocumentBuilderFactory liefert einen DOM-Parser. Der DOM-Parser hat eine Methode parse().
Beispiel <pricelist> <coffee> <name> Mocha Java Java </name> <price> 11.95 </price> </coffee> </pricelist> Gib des Preis des Kaffees Mocha Java aus!
DOM-Methoden Methoden NodeList coffeenodes = document.getelementsbytagname("coffee"); NodeList coffeenodes = document.getelementsbytagname("coffee"); Direkter Zugriff auf bestimmte Elemente mit getelementsbytagname möglich. Resultat ist eine NodeList. <?xml version="1.0"?>?> <pricelist> <coffee> <name>mocha Java</name> <price>11.95</price> </coffee> </pricelist>
DOM-Methoden Methoden NodeList coffeenodes = document.getelementsbytagname("coffee"); for for (int (inti=0; i=0; i i < coffeenodes.getlength(); i++) i++) {{ thiscoffeenode = coffeenodes.item(i); <?xml version="1.0"?>?> }} <pricelist> <coffee> <name>mocha Java</name> <price>11.95</price> </coffee> coffeenodes.item(0) </pricelist>
DOM-Methoden Methoden NodeList coffeenodes = document.getelementsbytagname("coffee"); for for (int (inti=0; i=0; i i < coffeenodes.getlength(); i++) i++) { thiscoffeenode = coffeenodes.item(i); Node thisnamenode = thiscoffeenode.getfirstchild(); String data data = thisnamenode.getfirstchild().getnodevalue(); if if (data.equals("mocha Java")) { Node thispricenode = thiscoffeenode.getnextsibling(); String price price = thispricenode.getfirstchild().getnodevalue(); <?xml version="1.0"?>?> break; } } <pricelist> <coffee> firstchild <name>mocha Java</name> <price>11.95</price> </coffee> </pricelist>
DOM-Methoden Methoden NodeList coffeenodes = document.getelementsbytagname("coffee"); for for (int (inti=0; i=0; i i < coffeenodes.getlength(); i++) i++) { thiscoffeenode = coffeenodes.item(i); Node thisnamenode = thiscoffeenode.getfirstchild(); String data data = thisnamenode.getfirstchild().getnodevalue(); if if (data.equals("mocha Java")) { Node thispricenode = thiscoffeenode.getnextsibling(); price <?xml version="1.0"?>?> String price = thispricenode.getfirstchild().getnodevalue(); <pricelist> break; } } firstchild <coffee> <name>mocha Java</name> <price>11.95</price> </coffee> </pricelist> Beachte: getfirstchild() liefert den Text-Knoten, nicht dessen Inhalt Mocha Java!
DOM-Methoden Methoden NodeList coffeenodes = document.getelementsbytagname("coffee"); for for (int (inti=0; i=0; i i < coffeenodes.getlength(); i++) i++) { thiscoffeenode = coffeenodes.item(i); Node thisnamenode = thiscoffeenode.getfirstchild(); String data data = thisnamenode.getfirstchild().getnodevalue(); if if (data.equals("mocha Java")) { Node thispricenode = thisnamenode.getnextsibling(); String price price = thispricenode.getfirstchild().getnodevalue(); <?xml version="1.0"?>?> break; } } <pricelist> <coffee> <name>mocha Java</name> nextsibling <price>11.95</price> </coffee> </pricelist>
DOM-Methoden Methoden NodeList coffeenodes = document.getelementsbytagname("coffee"); <?xml version="1.0"?>?> for (int i=0; i i++) <pricelist> for (int i=0; i < coffeenodes.getlength(); i++) { <coffee> thiscoffeenode = coffeenodes.item(i); <name>mocha Java</name> Node thisnamenode = thiscoffeenode.getfirstchild(); <price>11.95</price> String data data = thisnamenode.getfirstchild().getnodevalue(); </coffee> firstchild if if (data.equals("mocha Java")) { </pricelist> Node thispricenode = thisnamenode.getnextsibling(); String price = thispricenode.getfirstchild().getnodevalue(); break; } }
Vor- und Nachteile von DOM + Kontext (Parse-Baum) muss nicht von der Anwendung verwaltet werden. + Auch direkter Zugriff auf Elemente und Attribute über ihre Namen (unabhängig von deren Position) möglich. + Nicht nur Parsen von XML-Dokumenten möglich, sondern auch Modifikation oder Erstellung von Dokumenten. speicherintensiv, insbesondere bei großen XML-Dateien Abstrahiert nur unzureichend von der spezifischen XML- Syntax.
SAX oder DOM? SAX ist geeignet, um gezielt bestimmte Teile von XML- Dokumenten herauszufiltern, ohne dass zu einem späteren Zeitpunkt andere Teile des Dokumentes benötigt werden. DOM ist geeignet, um auf unterschiedliche Teile eines XML-Dokumentes zu verschiedenen Zeitpunkten zuzugreifen. DOM ist zum Erstellen und Modifizieren von Dokumenten geeignet.
Schema-Übersetzer Datenabstraktion XML- Schema übersetzen Java- Klassen Instanz validieren Instanzen XML- Dokument deserialisieren serialisieren Java- Objekte Java Architecture for XML Binding (JAXB)
Marshalling/Unmarshalling XML- Dokument marshal serialisieren deserialisieren unmarshal Java- Objekte Deserialisieren/Unmarshalling erstellt keinen Parse-Baum, sondern Java-Objekte mit Methoden zum Zugreifen und Modifizieren der Daten. Marshalling = führen, geleiten
Beispiel <pricelist> <coffee> <name>mocha Java</name> <price>11.95</price> </coffee> </pricelist> PriceList-Schema JAXB public interface PriceList { java.util.list getcoffee(int index); public interface CoffeeType { String getname(); void voidsetname(string value) java.math.bigdecimal getprice(); void voidsetprice(java.math.bigdecimal value) } }
Weiterführende Informationen zu JAXB http://java.sun.com/xml/jaxb/
Zusammenfassung Ein Parser analysiert XML-Dokumente und erstellt Parse- Bäume mit Tags, Text-Inhalten und Attribut-Wert-Paaren als Knoten. Ein Serialisierer generiert aus interner Datenstruktur einer Anwendung ein XML-Dokument. Bei einem Pull-Parser hat die Anwendung die Kontrolle über das Parsen, bei einem Push-Parser übernimmt der Parser selbst die Kontrolle. Einschritt-Parser analysieren das vollständige Dokument in einem einzigen Schritt, Mehrschritt-Parser analysieren die einzelnen syntaktischen Einheiten Schritt für Schritt.
Zusammenfassung SAX (Simple API for XML) ist ein API für das Push- Mehrschritt-Parsen von XML-Dokumenten. Ereignisbasiertes Parsen von SAX wird mit Call-Back- Methoden realisiert. DOM (Document Object Model) ist eine abstrakte Schnittstelle zum Zugreifen, Modifizieren und Erstellen von XML-Parse-Bäumen. Im Ergebnis ist DOM ein Einschritt-Pull-Parser. DOM ist ein W3C-Standard, SAX ein de facto Standard.
Zusammenfassung SAX ist geeignet, um gezielt bestimmte Teile von XML- Dokumenten herauszufiltern, ohne dass zu einem späteren Zeitpunkt andere Teile des Dokumentes benötigt werden. DOM ist geeignet, um auf unterschiedliche Teile eines XML-Dokumentes zu verschiedenen Zeitpunkten zuzugreifen. DOM ist zum Erstellen und Modifizieren von Dokumenten geeignet.
Zusammenfassung Immer Anwendungslogik durch spezielle Schicht von dem Datenzugriff trennen (sowohl bei SAX, als auch bei DOM). Schema-Übersetzer wie JAXB (Java Architecture for XML Binding) generieren eine solche Datenzugriffsschicht automatisch.