Typische Anwendungsfälle & Beispiele


... [ Seminar ] ... [ DOM & JDOM ] ... [ Fazit ] ...

Übersicht

In diesem Abschnitt werden typische Anwendungsfälle im Bereich der XML-Verarbeitung untersucht. Hierbei wird nach einer kurzen Beschreibung des Problems, jeweils eine Lösung für DOM (unter Verwendung von Xerces), beziehungsweise für JDOM angegeben. Am Ende dieser Seite finden sich Links für zwei vollständige Beispiele.

Bestehende XML-Dokumente lesen
Ein Dokument komplett neu erzeugen
Informationen extrahieren
Die Struktur des Dokuments verändern
Das Dokument speichern
Vollständige Beispiele


Bestehende XML-Dokumente lesen

Situation

Häufig möchte man aus einem eigenen Programm ein bestimmtes XML-Dokument einlesen, um es in irgendeiner Form zu bearbeiten. Ferner ist es häufig nötig, die zu lesenden Daten auf ihre Validität hinsichtlich der Dokumenttypdefinition zu überprüfen.

DOM

import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;

...
Document doc = null;
try {
 
DOMParser p = new DOMParser();   // Parser instanzieren
  // Validierung anschalten
 
p.setFeature("http://xml.org/sax/features/validation",true);
 
p.parse(args[0]);                // Datei parsen
  doc = p.getDocument();           // Dokument vom Parser abholen
}
catch (IOException io) {           // z. B. Dateifehler
  ...
}
catch (SAXException s) {           // z. B. ungültiges XML-Dokument
  ...
}

...

JDOM

import org.jdom.input.SAXBuilder;
import org.jdom.Document;

...
Document doc = null;
try {
  SAXBuilder b = new SAXBuilder(true);  // validierenden Parser nutzen
  doc = b.build(new File(args[0]));
}
catch (JDOMException j) {    // nur eine Ausnahme für alle Situationen
  ...
}
...

Fazit

Eine Schwäche von DOM besteht darin, dass das Einlesen eines Dokuments nicht durch das W3C spezifiziert ist und damit implementierungsabhängig bleibt. Dadurch ist die Portabilität der Programme in gewisser Weise eingeschränkt, da ein Wechsel einer DOM-Implementierung nicht immer ohne Codeänderungen durchgeführt werden kann. Auch ansonsten wirkt das JDOM-Programmfragment deutlich natürlicher und intuitiver.


Ein Dokument komplett neu erzeugen

Situation

Möchte man kein bereits vorhandenes Dokument lesen, dann steht man vielleicht vor dem Problem ein Dokument von Grund auf neu zu erzeugen und mit Elementen, Attributen, etc. zu füllen. Die unten stehenden Programmfragmente erzeugen jeweils ein XML-Dokument, welches der folgenden Struktur entspricht.

<linux-config>
  <window-manager default="true">
    <name>Enlightenment</name
  </window-manager
  <!-- etc -->
</linux-config>

DOM

import org.apache.xerces.dom.DOMImplementationImpl;
import org.w3c.dom.*;

...
DOMImplementation impl = DOMImplementationImpl.getDOMImplementation();
// Dokumentenobjekt erzeugen
Document
doc = impl.createDocument(null, "Linux", null);
// Erzeugen des Wurzelelements.
Element root = doc.createElement("linux-config");

// erstes Element und Kindknoten erzeugen und an die Wurzel hängen
Element wm = doc.createElement("window-manager");
Element n = doc.createElement("name");
Text name = doc.createTextNode("Enlightenment");
wm.setAttribute("default", "true");
n.appendChild(name);
wm.appendChild(n); 
root.appendChild(wm);

// Kommentar erzeugen und anhängen
root.appendChild(doc.createComment(" etc "));
...

JDOM

import org.jdom.*;

...
Element root = new Element("linux-config");  // Wurzelelement erzeugen
Document doc = new Document(root);       // neues Dok. benötigt nur Wurzel

root        // Dokumentstruktur erzeugen
  .addContent(new Element("window-manager")
    .addAttribute("default","true")
    .addContent(new Element("name")
      .setText("Enlightenment")))
  .addContent(new Comment(" etc "));
...

Fazit

Hier kann man sicherlich geteilter Meinung sein, ob die direkte Instanzierung von Objekten die gute Lesbarkeit der Quellen fördert. Ein bedeutenderer Unterschied, der hier sichtbar wird, ist die Behandlung des Textinhalts von Elementen. Während das DOM diesen Textinhalt als echte Kindknoten eines Elements betrachtet, behandelt JDOM diesen Text als Eigenschaft eines Elements. Dies wird im folgenden Abschnitt noch deutlicher.


Informationen extrahieren

Situation

Natürlich besteht eine der Hauptaufgaben im Umgang mit XML darin, die im Dokument enthaltenen Informationen zu extrahieren und weiterzuverwenden.

<league>
  <team>
    <name>Real Madrid</name
    <points>23</points>
    <goals own="36" against="11"/>
  </team
</league>

Die Aufgabe der folgenden Programmfragmente besteht nun darin den Mannschaftsnamen, die Punkte und die Tordifferenz in geeigneten Variablen zu speichern. Hierzu nehmen wir ferner an, dass die Variable root das Wurzelelement des Dokuments (<league>) enthält. 

DOM

import org.w3c.dom.*;

...
try {
  Element t = (Element) root.getElementsByTagName("team").item(0);

  // Name der Mannschaft als String speichern
  Node n = t.getElementsByTagName("name").item(0);
  String name = n.getFirstChild().getNodeValue();

  // Anzahl der Punkte als int speichern
  Node p = t.getElementsByTagName("points").item(0);
  int pts = Integer.parseInt(p.getFirstChild().getNodeValue());

  // Tordifferenz als int speichern
  Element g = (Element) t.getElementsByTagName("goals").item(0);
  Attr own = g.getAttributeNode("own"); 
  Attr ags = g.getAttributeNode("against"); 
  int diff = Integer.parseInt(own.getValue()) -
             Integer.parseInt(ags.getValue());
}
catch (Exception e) {  // Ausnahmebehandlung hier nur rudimentär
  ...
}
...

JDOM

import org.jdom.*;

...
try {
  Element t = root.getChild("team");

  // Name der Mannschaft als String speichern
  String name = t.getChild("name").getText();

  // Anzahl der Punkte als int speichern
  int pts = Integer.parseInt(t.getChild("points").getText());

  // Tordifferenz als int speichern
  int diff = t.getChild("goals").getAttribute("own").getIntValue() -
             t.getChild("goals").getAttribute("against").getIntValue();
}
catch (Exception e) {  // Ausnahmebehandlung hier nur rudimentär
  ...
}
...

Fazit

Spätestens an dieser Stelle wird der Mehraufwand, der durch die Verwendung von DOM notwendig wird beachtlich. So gibt es zum Beispiel keine Möglichkeit eine Referenz auf das erste Kindelement eines Knotens mit gegebenem Namen zu erhalten, ohne zuvor eine Liste aller dieser Kindelemente zu erzeugen. Außerdem wirkt sich auch hier der Unterschied bei der Handhabung des Textinhalts von Elementen bei den beiden Werkzeugen zum Nachteil des DOM aus. Auch die Bereitstellung von einigen Bequemlichkeitsmethoden bei JDOM, gerade bei der Bearbeitung von Attributen, hilft dem Programmierer bei häufigen Aufgaben.


Die Struktur des Dokuments verändern

Situation

Zuweilen steht man vor der Aufgabe, dass die Struktur des Dokumentenbaums verändert werden muss. Neue Elemente werden eingefügt oder bestimmte Attribute entfernt. In den folgenden Programmfragmenten werden die Kindelemente eines bestimmten Elements nach einem bestimmten Kriterium sortiert.  

<league>
  <team>
    <name>Real Madrid</name
    <points>4</points>
    <goals own="3" against="2"/>
  </team
  <team>
    <name>Bayern München</name
    <points>9</points>
    <goals own="5" against="1"/>
  </team
  <team>
    <name>Dynamo Kiew</name
    <points>0</points>
    <goals own="2" against="6"/>
  </team
  <team>
    <name>Lazio Rom</name
    <points>4</points>
    <goals own="3" against="4"/>
  </team
</league>

DOM

import org.w3c.dom.*;

...
NodeList teams = root.getElementsByTagName("team");
// die Elemente mit "Selection-Sort" sortieren
for (int i=0; i<positions.getLength(); ++i) {
  // die beste verbliebene Mannschaft suchen
 
Element first = (Element) positions.item(i);
  Element max = first; 
  for (int j=i+1; j<positions.getLength(); ++j) {
    Element cand = (Element) positions.item(j);
    if (comparePositions(max, cand) > 0) max = cand;
  }
  // Elemente tauschen
  if (first != max) { 
    Node n = root.removeChild(max);
    root.insertBefore(n,first);
  }

...

JDOM

import java.util.*;
import
org.jdom.*;

...
List teams = root.getChildren("team");  // Liste aller Mannschaften aufbauen
// unter Verwendung unseres konkreten Comparators sortieren lassen
Collections.sort(teams, new LigaComparator());  
root.setChildren(teams);  // sortierte Liste als Kinder der Wurzel einsetzen
...

Fazit

Dieses Beispiel zeigt ganz deutlich, wie sich die Verwendung der Standardkollektionen durch JDOM sehr positiv auf Kriterien wie Flexibilität, Performanz, Programmieraufwand, Sicherheit, Lesbarkeit, Motivation, etc. auswirkt.


Das Dokument speichern

Situation

Nach der Erzeugung oder Verarbeitung eines Dokuments, soll dieses wohl meist als XML-Dokument in einer bestimmten Datei gespeichert werden. Dies ermöglicht es in einer späteren Sitzung die Daten erneut einzulesen und zu bearbeiten.

DOM

import java.io.*;
import
org.w3c.dom.*;
import
org.apache.xml.serialize.XMLSerializer;

...
try {
  FileOutputStream out = new FileOutputStream(args[0]);
  XMLSerializer serializer = new XMLSerializer(out,null);
  serializer.serialize(root);
  out.flush();
  out.close();
}
catch (IOException e) { 
  ...
}
...

JDOM

import java.io.*;
import
org.jdom.*;
import org.jdom.output.XMLOutputter;

...
try {
  FileOutputStream out = new FileOutputStream(args[0]);
  XMLOutputter serializer = new XMLOutputter();
  serializer.output(doc,out);
  out.flush();
  out.close();
}
catch (IOException e) { 
  ...
}
...

Fazit 

Die Lösung des Problems ist in beiden Fällen unkompliziert und sehr ähnlich. Im übrigen gilt hier das gleiche wie für das Einlesen eines Dokuments: es ist unbedingt zu beachten, dass man sich bei der Verwendung von DOM auf eine bestimmte Implementierung (hier: Xerces) festlegen muss, um das Dokument zu Serialisieren, da das W3C in dieser Hinsicht keine Standardisierung vorgenommen hat.


Vollständige Beispiele

An dieser Stelle folgen noch zwei eigene Programme im vollen Quelltext, aus denen die zuvor behandelten Problemfälle zum großen Teil entnommen sind. Da die Programme kommentiert sind, wird hier auf eine ausführliche Beschreibung verzichtet. Es ist wichtig, dass sich sowohl zum Kompilieren, als auch zur Laufzeit, die notwendigen Pakete im aktuellen Klassenpfad befinden.

Theatre

Beschreibung:
Dieses Programm gibt für ein Theaterstück eine Liste der Szenen aus, in denen eine bestimmte Figur auftritt.

Dieses ist ein Beispiel für eine Aufgabe, die sich auch mit SAX lösen lässt, da ein sequentieller, nur-lesender Zugriff auf die Daten ausreichend ist. Die Verwendung von SAX ist sowohl im Hinblick auf die Speicher-, als auch auf die Laufzeiteffizienz optimal, und sollte wann immer möglich erfolgen. 

Beispielaufruf:
    javac Theatre1.java
    java Theatre1 rich_iii.xml GLOUCESTER

    ACT I - SCENE I. London. A street.
    ACT I - SCENE II. The same. Another street.
    ACT I - SCENE III. The palace.
    ACT II - SCENE I. London. The palace.
    ACT II - SCENE II. The palace.
    ACT III - SCENE I. London. A street.
    ACT III - SCENE IV. The Tower of London.
    ACT III - SCENE V. The Tower-walls.
    ACT III - SCENE VII. Baynard's Castle.

Links: 
Das Programm unter Verwendung von...

Zwei Theaterstücke: rich_iii.xml  two_gent.xml

League

Beschreibung:
Dieses Programm liest entweder eine Liste von Mannschaften von der Standardeingabe, oder einen existierenden Tabellenstand aus einem XML-Dokument ein. Danach wird für jede Mannschaft ein Ergebnis eingelesen, um dann einen neuen Tabellenstand zu berechnen.

Links:
Das Programm unter Verwendung von...


... [ Seminar ] ... [ DOM & JDOM ] ... [ Beispiele ] ... [ Fazit ] ...