XML+Java: DOM

Werbung
XML-Verarbeitung mit DOM
Holger Jakobs – [email protected], [email protected]
2011-02-02
Inhaltsverzeichnis
1 Einführung in DOM
1.1 Parsen von XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Erzeugen eines neuen Dokuments . . . . . . . . . . . . . . . . . . . . . . .
2 Fortgeschrittene Anwendung von DOM
2.1 Schema-Validation mit Xerces . . . .
2.2 Anwendung von XSLT . . . . . . . .
2.3 Entfernen von Elementen . . . . . . .
2.4 Verschieben von Elementen . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
2
3
4
4
7
10
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 DOM wird das gesamte Dokument eingelesen und kann im Speicher beliebig oft
gelesen und verändert werden, bevor es dann wieder – in welcher 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 SAX1 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 Einführung in DOM
DOM2 ist die Abkürzung für Document Object Model“, ein plattform- und program”
miersprachenneutrales Interface des Word Wide Web Consortiums3 zur Verarbeitung von
1) http://www.bg.bib.de/portale/xml/pdf/XML-SAX.pdf
2) http://w3.org/DOM
3) http://w3.org
1
1.1 Parsen von XML
1 EINFÜHRUNG IN DOM
XML-Daten. Es kann mit compilierten und mit interpretierten Sprachen verwendet werden, wobei Zugriff auf Teile und Veränderung des Dokuments möglich sind. Das Ergebnis
der Dokumentverarbeitung kann in das XML-Dokument zurückgeschrieben werden – oder
auch in ein anderes.
Eine ausführliche Beschreibung von DOM gibt es bei Coverpages4 , ein Tutorial gibt es
bei Sun5 .
DOM4J6 ist eine handliche, quelloffene Bibliothek für das Arbeiten mit XML, XPath und
XSLT auf der Java-Plattform. Sie steht im XML-Portal7 zum Download zur Verfügung;
Sie brauchen das jar-Archiv lediglich Ihrem CLASSPATH hinzuzufügen.
1.1 Parsen von XML
Im ersten Beispiel soll ein XML-Dokument, dessen Name als Parameter übergeben wird,
geparst werden. Der Name des Wurzelelementes und die Namen aller direkten Kindelemente
davon werden ausgegeben.
import
import
import
import
import
org.dom4j.Document;
org.dom4j.Element;
org.dom4j.DocumentException;
org.dom4j.io.SAXReader;
java.util.Iterator;
public class ParseXML {
public static void main (String args[]) throws Exception {
// Eingabedateien verarbeiten
for (int i=0; i < args.length; i++) {
SAXReader reader = new SAXReader();
Document document = reader.read (args[i]);
iterate (document);
} // for
} // main()
public static void iterate (Document d) throws DocumentException {
Element wurzel = d.getRootElement();
System.out.println ("Das Wurzelelement heißt "+
wurzel.getName());
System.out.println ("und hat folgende direkte Kindelemente:");
4)
5)
6)
7)
http://xml.coverpages.org/dom.html
http://java.sun.com/xml/jaxp/dist/1.1/docs/tutorial/dom/
http://dom4j.sourceforge.net/
http://www.bg.bib.de/portale/xml/Quelltexte
2
1 EINFÜHRUNG IN DOM
1.2 Erzeugen eines neuen Dokuments
for (Iterator i = wurzel.elementIterator(); i.hasNext(); ) {
Element element = (Element) i.next();
System.out.println ("Element: " +element.getName());
} // for
} // iterate()
} // class ParseXML
Iteratoren sind ein gängiges Verfahren von Java, um Listen abzuarbeiten, schlagen Sie sie
ggf. in der Java-Online-Doku nach oder fragen Sie im Java-Unterricht.
Mit diesem kleinen Programm kann man schon erkennen, wie man an einzelne Elemente
aus dem XML-Baum herankommt. Das Interface Element hat viele Methoden, um Eigenschaften eines Elementes abzufragen, z. B. wie oben angewendet nach dem Namen, aber
auch nach dem Inhalt, nach Attributen, den enthaltenen Elementen usw. usf.
1.2 Erzeugen eines neuen Dokuments
In diesem Abschnitt sehen Sie auch, wie Sie ein bereits bestehendes, d. h. eingelesenes
Dokument verändern können. Da ein neues Dokument zunächst leer ist, müssen Sie vor
dem Schreiben in eine Datei auch hier Veränderungen vornehmen – allerdings ist es hier auf
das Hinzufügen von Elementen und Attributen beschränkt. Das Löschen oder Verändern
von vorhandenen Elementen und Attributen können Sie leicht in der API-Dokumentation
von DOM4J nachschlagen.
import
import
import
import
import
org.dom4j.Document;
org.dom4j.DocumentHelper;
org.dom4j.Element;
org.dom4j.io.XMLWriter;
org.dom4j.io.OutputFormat;
import java.io.FileWriter;
import java.io.IOException;
public class NewDoc {
public static void main (String[] args) {
Document neu = createDocument();
try {
writeDocument (neu);
} catch (Exception e) {
System.out.println ("SAXException: " + e.getMessage());
System.exit (1);
} // try/catch
3
2 FORTGESCHRITTENE ANWENDUNG VON DOM
} // main()
public static Document createDocument() {
Document document = DocumentHelper.createDocument();
Element root = document.addElement( "Autorenliste" );
root.addElement ("Autor")
.addAttribute ("Initialen", "JW")
.addAttribute ("Ort", "Kiel")
.addText ("Johann Werkzeugmacher");
root.addElement ("Autor")
.addAttribute ("Initialen", "HS")
.addAttribute ("Ort", "Bonn")
.addText ("Hermann Schwaderlappen");
return document;
} // createDocument()
public static void writeDocument (Document d)
throws IOException {
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter (
new FileWriter ("neu.xml"), format);
writer.write (d);
writer.close();
} // writeDocument()
}
2 Fortgeschrittene Anwendung von DOM
2.1 Schema-Validation mit Xerces
Weder die Validation mit DTDs noch die Verwendung des crimson-Parsers sind technisch
der letzte Stand. Zwar gilt crimson als der Parser, der garantiert in jeder Java-Umgebung
zur Verfügung steht, der Xerces-Parser hat aber einen deutlich größeren Funktionsumfang,
zu dem auch die Möglichkeit gehört, mittels Schemas zu validieren.
Einem XmlTester-Konstruktor übergibt man einen Dateinamen bzw. einen URL des
XML-Dokuments und als zweiten Parameter wahlweise den Dateinamen bzw. URL des
Schemas oder aber null. Im letzteren Fall muss das XML-Dokument einen Verweis auf
das zu verwendende Schema tragen – sonst findet mangels eines Schemas keine Validation
statt.
4
2 FORTGESCHRITTENE ANWENDUNG VON DOM
2.1 Schema-Validation mit Xerces
Mit Hilfe einer DocumentBuilderFactory wird ein DocumentBuilder erzeugt. Dessen
Methode parse kann man einen Dateinamen oder einen URL übergeben. Rückgabewert
von parse ist ein DOM-Document, d. h. ein Objekt einer Klasse, welche das DocumentInterface implementiert. Ein solches Document repräsentiert ein komplettes HTML- oder
XML-Dokument in einem Java-Objekt. Es ist quasi die Wurzel des Dokumentbaums und
ermöglicht den Zugriff auf die Daten des Dokuments.
Wegen der factory-Einstellungen mit setNamespaceAware, setValidating und setAttribute vor dem Erzeugen des DocumentBuilder und dem Parsen des Dokuments führt
der Parser die Schema-Validation durch. Das erzeugte Document-Objekt wird lediglich dazu
verwendet, den Namen des Root-Nodes anzuzeigen. Weitere Methoden zum Lesen, zum
Verändern und zum Schreiben in eine neue XML-Datei stehen zur Verfügung – siehe JavaAPI-Dokumentation.
/* XmlTester.java
basierend auf Code aus
http://www.javaworld.com/javaworld/jw-08-2005/jw-0808-xml_p.html
kleine Aenderungen durch [email protected], 2006-03-02 */
import
import
import
import
import
import
import
import
import
java.io.IOException;
javax.xml.parsers.DocumentBuilder;
javax.xml.parsers.DocumentBuilderFactory;
javax.xml.parsers.ParserConfigurationException;
org.w3c.dom.Document;
org.w3c.dom.Node;
org.xml.sax.SAXException;
org.xml.sax.ErrorHandler;
org.xml.sax.SAXParseException;
public class XmlTester {
public static final void main (String[] args) {
if (args.length != 1 &&
args.length != 2) {
System.out.println ("Usage: java XmlTester myFile.xml [ mySchema.xsd ]\n");
System.out.print ("If a second argument is provided, the given ");
System.out.println (" schema file will be used.");
System.out.print ("Otherwise, the schema file location must be ");
System.out.println ("specified in the XML document.\n");
System.exit (-1);
} // if
String xmlFile = args[0];
String schemaFile = null;
if (args.length == 2) {
5
2.1 Schema-Validation mit Xerces
2 FORTGESCHRITTENE ANWENDUNG VON DOM
schemaFile = args[1];
} // if
try {
XmlTester xmlTester = new XmlTester (xmlFile, schemaFile);
} catch (Exception e) {
System.out.println (e.getClass().getName() +": "+ e.getMessage());
} // try/catch
} // main()
public XmlTester(String xmlFile, String schemaFile)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
System.out.println ("DocumentBuilderFactory: "
+ factory.getClass().getName());
factory.setNamespaceAware (true);
factory.setValidating (true);
factory.setAttribute (
"http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
if (schemaFile != null) {
factory.setAttribute (
"http://java.sun.com/xml/jaxp/properties/schemaSource",
schemaFile);
} // if
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler (new SimpleErrorHandler());
Document document = builder.parse (xmlFile);
Node rootNode = document.getFirstChild();
System.out.println ("Root node: "+ rootNode.getNodeName());
} // XmlTester()
} // class XmlTester
class SimpleErrorHandler implements ErrorHandler {
public void error (SAXParseException exception) {
System.out.println ("error: "+ exception.getMessage());
6
2 FORTGESCHRITTENE ANWENDUNG VON DOM
2.2 Anwendung von XSLT
} // error()
public void fatalError (SAXParseException exception) {
System.out.println ("fatalError: "+ exception.getMessage());
} // fatalError()
public void warning (SAXParseException exception) {
System.out.println ("warning: "+ exception.getMessage());
} // warning()
} // class SimpleErrorHandler
Damit der richtige Parser Xerces verwendet wird, muss dieser im CLASSPATH vor dem alten
Crimson-Parser liegen. Fehlt der Xerces oder liegt er im CLASSPATH weiter hinten, so wird
der Crimson-Parser verwendet, der aber die Attribute nicht kennt und daher einen Fehler
liefern wird: No attributes are implemented
Die Java-VM liest alle im CLASSPATH angegebenen Jar-Archive und schaut dort nach
der Datei META-INF/services/javax.xml.parsers.DocumentBuilderFactory. In dieser
steht dann der komplette Pfad der Klasse, die die DocumentBuilderFactory implementiert.
Wird also das zu Xerces gehörende Archiv (xercesImpl.jar) vor dem Crimson-Archiv
durchsucht, wird Xerces verwendet.
2.2 Anwendung von XSLT
Als weiteres Beispiel soll die Verwendung von XSLT in einem eigenen Java-Programm gezeigt werden. Es ist also festzustellen, dass die Verarbeitung mit XSLT Stylesheets und
die in eigenen Programmen keineswegs einander ausschließen, sondern auch miteinander
kombiniert werden können. Manche Verarbeitungsschritte lassen sich leichter in XSLT codieren, andere leichter mit Java programmieren. Änderungen an XSLT bedürfen keiner
Compilation, die am Java-Code schon. So hat alles seine Vor- und Nachteile, die Sie in der
Praxis abwägen müssen.
Das Java-Programm StyleXSLT.java sieht so aus:
import
import
import
import
org.dom4j.io.SAXReader;
org.dom4j.Document;
org.dom4j.io.DocumentResult;
org.dom4j.io.DocumentSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import org.dom4j.io.XMLWriter;
import org.dom4j.io.OutputFormat;
7
2.2 Anwendung von XSLT
2 FORTGESCHRITTENE ANWENDUNG VON DOM
import java.io.FileWriter;
import java.io.IOException;
public class StyleXSLT {
public static void main (String[] args) {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer (
new StreamSource ("wreport-style.xml"));
SAXReader reader = new SAXReader();
Document document = reader.read ("wetter.xml");
DocumentSource src = new DocumentSource (document);
DocumentResult res = new DocumentResult();
t.transform (src, res);
Document transformedDoc = res.getDocument();
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter (
new FileWriter ("wetter.html"), format);
writer.write (transformedDoc);
writer.close();
} catch (Exception e) {
System.out.println ("SAXException: " + e.getMessage());
System.exit (1);
} // try/catch
} // main()
}
Die TransformerFactory wird herangezogen, um einen neuen Transformer namens t zu
erzeugen, der das Stylesheet wreport-style.xml verwendet. Das Dokument wetter.xml
wird mit Hilfe eines SAXReaders in ein DOM-Document eingelesen. Es wird je eine Instanz
von DocumentSource und DocumentResult für den Transformationsvorgang gebildet und
mittels der Methode transform der Klasse Transformer die XSLT-Transformation durchgeführt. Das transformierte Dokument wird aus dem Ergebnis gelesen mittels der Methode
getDocument().
Damit das Ergebnis schön formatiert ausgegeben wird, wird ein entsprechendes OutputFormat generiert und anschließend das Dokument mit einem XMLWriter ausgegeben. Das
Zieldokument ist ein HTML-Dokument, wie man dem folgenden Stylesheet wreport-style.
8
2 FORTGESCHRITTENE ANWENDUNG VON DOM
2.2 Anwendung von XSLT
xml entnehmen kann:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:apply-templates select="weather-report"/>
</xsl:template>
<xsl:template match="weather-report">
<html>
<head><title>hi there</title></head>
<body> <!-- Fuer eine Auswahl von Elementen Vorlagen anwenden: -->
<xsl:apply-templates select="area|date|time|measurements"/>
</body>
</html>
</xsl:template>
<!-- Pattern = Knoten[area|date|time|measurements] -->
<xsl:template match="date|time"> <!--Fuer date ODER time-->
<p style="color:navy; background-color:yellow">
<xsl:value-of select="." /> <!-- fuege Wert(e) ein -->
</p>
</xsl:template>
<xsl:template match="area">
<p style="color:yellow; background-color:blue">
<!-- Inhalt des Kindes "country" -->
<xsl:value-of select="country" />
</p>
</xsl:template>
<xsl:template match="measurements">
<p style="color:navy; background-color:yellow">
<!-- Inhalte aller direkten Kinder -->
<xsl:value-of select="./*" />
</p>
</xsl:template>
</xsl:stylesheet>
Das passende XML-Dokument wetter.xml sieht so aus:
9
2.3 Entfernen von Elementen 2 FORTGESCHRITTENE ANWENDUNG VON DOM
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="wreport-style.xml" ?>
<weather-report>
<date>Juni 06, 2000</date>
<time>09:10</time>
<area>
<!-- Dies ist ein Kommentarknoten -->
<city>Köln</city>
<region >NRW</region>
<country>Germany</country>
</area>
<measurements>
<skies>fine</skies>
<temperature scale="C">15</temperature>
<wind>
<direction>SW</direction>
<windspeed>6</windspeed>
</wind>
</measurements>
</weather-report>
Wie das erzeugte Dokument wetter.html aussieht, können Sie dem XSLT-Code entnehmen oder auch einfach ausprobieren. Die Dokumente stehen im XML-Portal8 im Bereich
Quelltexte zur Verfügung.
2.3 Entfernen von Elementen
Um in der Familiendatei alle Dackel zu entfernen, iteriert man über alle Familien und
innerhalb der Familien über alle Hunde. Falls bei Rasse“ der Wert Dackel“ eingetragen
”
”
ist, hängt man das Element aus dem XML-Dokument mittels der Methode detach() aus.
Das sieht im Code so aus:
public static void entferneDackel (Document d)
throws DocumentException {
Element wurzel = d.getRootElement();
// Schleife über Familien
for (Iterator iFam = wurzel.elementIterator("familie"); iFam.hasNext();) {
Element fam = (Element) iFam.next();
System.out.println ("\nFamilie " + fam.element("name").getText());
for (Iterator iHund = fam.elementIterator ("hund"); iHund.hasNext();) {
8) http://www.bg.bib.de/portale/xml
10
2 FORTGESCHRITTENE ANWENDUNG VON DOM2.4 Verschieben von Elementen
Element hund = (Element) iHund.next();
System.out.print ("hat Hund " + hund.element("name").getText());
String rasse = hund.element("rasse").getText();
System.out.println (" der Rasse " + rasse);
if ("Dackel".equals (rasse)) {
hund.detach();
System.out.println (" abgehängt");
} // if
} // for iHund
} // for iFam
} // entferneDackel()
Das gesamte Programm finden Sie im XML-Portal unter DackelTod.java.
2.4 Verschieben von Elementen
Oft ist bei der Verarbeitung von XML-Dokumenten die Verschiebung von Elementen bzw.
ganzen Teilbäumen notwendig. Durch die Darstellung von Teilen der Dokumentstruktur
in Form von Java Collections (z. B. Listen) kann man erreichen, dass man lediglich mit
letzteren arbeiten muss und sich alle Änderungen an einer Liste unmittelbar im Dokument
auswirken.
public static void bucheDackelUm (Document d)
throws DocumentException {
Element wurzel = d.getRootElement();
Element dackel = null;
// Schleife über Familien
for (Iterator iFam = wurzel.elementIterator("familie"); iFam.hasNext();) {
Element fam = (Element) iFam.next();
// Wenn nicht Fam. Meier, dann nächste Familie
if (!"Meier".equals(fam.element("name").getText())) continue;
// Dackel suchen, kopieren und im Original entfernen
List lHunde = fam.elements("hund");
for (Iterator iHund = lHunde.iterator(); iHund.hasNext();) {
Element hund = (Element) iHund.next();
// ist der aktuelle Hund ein Dackel?
if ("Dackel".equals (hund.element("rasse").getText())) {
dackel = hund;
lHunde.remove (hund);
break; // gefunden, keine weitere Suche
11
2.4 Verschieben von Elementen2 FORTGESCHRITTENE ANWENDUNG VON DOM
} // if
} // for iHund
} // for iFam
// wurde ein Hund in der Variablen dackel gespeichert?
if (dackel != null) {
// Es wurde ein Dackel gefunden, diesen jetzt bei Familie Mangen einhängen
for (Iterator iFam = wurzel.elementIterator("familie"); iFam.hasNext();) {
Element fam = (Element) iFam.next();
if ("Mangen".equals(fam.element("name").getText())) {
// Bei Fam. Mangen eine Liste der Hunde (momentan nur einer) erstellen
List lHunde = fam.elements("hund");
// den Dackel an die Liste anhängen
lHunde.add (dackel);
} // if
} // for iFam
} // if
} // bucheDackelUm()
Das gesamte Programm finden Sie im XML-Portal unter DackelAnMangen.java. Der dortige Quelltext enthält auch noch weitere Varianten, wie man diese Aufgabe programmtechnisch lösen kann.
$Id: XML-DOM.tex,v a5120cdeb070 2008/11/06 11:45:29 bibjah $
12
Herunterladen