1.1. Web-Services

Werbung
Web – Services
Java-API-JAXP
Dokumentation
zum Referat „Java-API-JAXP“
Referenten :
 Mark Vackiner
 Fares Abdelmounaim
 Thomas Hahne
 Dominik Hanft
 Martin Simmermacher
Erstellt von Martin Simmermacher
Mittwoch, 4. Dezember 2002
-2-
Inhalt :
1. Einführung
1.1. Web-Services
1.2. Java-API
1.3. Parser
2. JAXP
2.1. Bestandteile
2.2. Vorraussetzung des Parsers
2.3. Grafische Darstellung
3. SAX
3.1. Funktionsweise
3.2. Handler
3.3. Beispiel
3.3.1. Funktion
3.3.2. Quell-Code
4. DOM
4.1. Funktionsweise
4.2. Spezifikation
4.3. Beispiel
4.3.1. Funktion
4.3.2. Quell-Code
5. XSLT
6. Anhang
6.1. SAX
6.2. DOM
-3-
1. Einführung
1.1. Web-Services
Durch das Internet und die Fortschritte im Bereich der Netztechnologien sind Services on
Demand möglich: Nutzung von Diensten von überall, zu jeder Zeit und mit jedem Endgerät.
Die Realisierung solcher Web Services erfordert neue Techniken und Protokolle sowie eine
Entwicklungsinfrastruktur, die auf Standards beruht.
Web Services sind dazu da um konsumiert zu werden! Die Erstellung eines Web Services ist
Dank des Microsoft SOAP Toolkits relativ einfach. Nach der Erstellung des Web Service soll
der Client aber auch die Dienste benutzen, d.h. konsumieren können. Anhand eines einfachen
Beispiels, soll dies mit Hilfe des SOAP Toolkits dargestellt werden.
Der "Konsum"-Prozess beginnt mit einem Request des Clients. Eben dieser Request wird vom
SoapClient Objekt entgegengenommen und erstellt einen SOAP Request der dem Web Server
übergeben wird. Der Server bearbeitet dann den Request, führt die erhaltenen Anweisungen
aus, und schickt das Ergebnis als SOAP Response wieder zurück zum Client. Dort formuliert
der SoapClient aus der SOAP Response eine, für den Benutzer des Web Service lesbare
Anwort, die das Ergebnis aus der Prozedur enthält.
Bildlich gesprochen passiert also folgendes:
-4-
1.2. Java-API
Hinter dem Namen API verbirgt sich das Application Programming Interface,
welches folgende Punkte in sich vereinigt :




Sammlung von Schnittstellen
Erleichtert die Erstellung von Software
Vereinfacht Zugriffe auf Bestandteile des Systems
Entwickler muss keine speziellen Systemkenntnisse haben
1.3. Parser
Ein Parser ist eine Software oder ein Teil davon, die den Datenstrom eines Dokumentes
analysiert und entsprechend der Syntax aufbereitet.
Beim Parsing werden die Informationen des Dokumentes in die Elemente gefiltert, in die die
Informationen strukturiert sind.
Um z.B. ein XML-Dokument zu parsen, erzeugt man sich zunächst eine Instanz eines SAXParsers. Dies geschieht über die ParserFactory, die gemäß der Fabrikmethode den
Erzeugungsprozeß des Parsers kapselt. Der Parser darf aus einem beliebigen XML-ParserPaket kommen und muß sich an die SAX-Spezifikation halten.
(Vorraussetzungen für einen Parser sh. Kap. 2.2. Vorraussetzungen des Parsers.)
-5-
2. JAXP
JAXP wurde von einem Projektteam unter der Führung von SUN entwickelt und stellt
Standard-Schnittstellen zum Parsen und Manipulieren von XML-Dokumenten zur
Verfügung. Allerdings überläßt es die Wahl eines bestimmten Parsers dem Programmierer,
der über einen sogenannten Plugability Layer den zu benutzenden Parser für das Parsen
festlegen kann.
Das ganze Paket ist ein "Java Optional Package" und hat den Paketnamen
javax.xml.parsers. Wie man an dem javax Präfix sieht, gehört es damit schon zu den
Standarderweiterungen des JDK( wie z.B. auch die swing-Pakete im Paket javax.swing.*),
wird zur Zeit aber noch nicht zusammen mit dem JSDK ausgeliefert.
2.1. Bestandteile
Zur Zeit besteht JAXP aus 6 Klassen, davon sind 4 abstrakt und definieren die
Schnittstellen für das Erzeugen eines SAXParsers bzw. DOMBuilders, sowie für das
Arbeiten mit Instanzen von ihnen. Die anderen zwei Klassen sind zur Fehlerbehandlung
vorgesehen.

SAXParserFactory: ist eine Klasse, nach dem Designmuster der Fabrikmethode, die
es Anwendungen erlaubt einen Parser nach der SAX-Spezifikaton zu konfigurieren
und eine Instanz zu erhalten.

SAXParser: kapselt den benutzten SAX-Parser, der gemäß des org.xml.saxParser
Interfaces implementiert sein muß. Methoden: parse und Infos über Parserstate. Ein
Aufruf von SAXParserFactory.newInstance() gibt immer eine Instanz dieser Klasse
zurück.

DocumentBuilderFactory: ähnlich der SAXParserFactory, nur wird hier ein Parser
instanziert und konfiguriert, der ein XML-Dokument parsed und ein DOM-Modell
liefert.

DocumentBuilder: kapselt den benutzten DOM-Builder, der von der
DocumentBuilderFactory bei Aufruf von newInstance() zurückgegeben wird.

FactoryConfigurationError: Falls es irgendein Problem bei der Konfiguration der
Factory-Klassen gibt, wird dieser Fehler aufgerufen.

ParserConfigurationException: Diese Exception tritt auf, wenn die Factory den
Parser nicht mit den vorgegebenen Parametern konfigurieren konnte.
-6-
2.2. Vorraussetzungen des Parsers
Um einen Parser in das JAXP-Paket "pluggen" zu können, muß dieser einige
Voraussetzungen erfüllen:

Er muß als Character Set encodings mindestens ascii, UTF-8 und UTF-16 unterstützen

Er muß ein Dokument auf seine Wohlgeformtheit überprüfen können

Er muß ein Dokument auf seine Gültigkeit anhand der DTD überprüfen können
2.3. Grafische Darstellung
SAX
Applikation
JAXP
XSLT
DOM
Plug in
Implemtationen
verschiedener
Hersteller
Kompilierte
Anwendung
-7-
3. SAX
SAX steht für "Simple API for XML" und definiert Schnittstellen für das Parsen von XMLDokumenten. Es ist kein W3C-Standard, sondern wurde von der XML-DEV Mailinglist
entwickelt und Anfang 1998 in der Version 1.0 herausgebracht. Zur Zeit ist die Version 2.0
aktuell.
Das SAX-API stellt einen Mechanismus zur seriellen Verarbeitung von XML-Dokumenten
zur Verfügung. Das heißt, dass ein Dokument Element um Element abgearbeitet wird. Die
Kommunikation mit anderen Anwendungen wird dabei über sogenannte Handler abgewickelt,
weshalb man auch von einem ereignisgesteuerten Protokoll spricht.
Die folgende Grafik illustriert dies:
-8-
3.1. Funktionsweise
Um ein XML-Dokument zu parsen, erzeugt man sich zunächst eine Instanz eines SAXParsers. Dies geschieht über die ParserFactory, die gemäß der Fabrikmethode den
Erzeugungsprozeß des Parsers kapselt. Der Parser darf aus einem beliebigen XML-ParserPaket kommen und muß sich an die SAX-Spezifikation halten.
Danach muß man die jeweiligen Handler(entweder generische von einem XML-Parser,
oder Handler, die man selbst gemäß den durch SAX gegebenen Interfaces implementiert
hat) erzeugen und beim Parser registrieren.
Zum Schluß ruft man die Methode parse des Parsers mit dem zu parsenden XMLDokument als Parameter auf. Der Parser wird nun das Dokument sequentiell lesen und je
nach Element, das er liest, bestimmte callback-Methoden in den Handlern aufrufen. Über
diese und vor allem den DocumentHandler läßt sich die Verarbeitung des XMLDokuments dann steuern.
3.2. Handler
Das SAX-API definiert folgende Handler zur Verarbeitung eines XML-Dokuments:

DocumentHandler: Dies ist der wichtigste Handler zur Verarbeitung überhaupt. In
ihm werden die Methoden startDocument und endDocument implementiert, die
jeweils zu Beginn und zum Ende eines Dokuments auftreten. Desweiteren gibt es die
Methoden startElement und endElement, die aufgerufen werden, wenn der Parser ein
einleitendes Tag bzw. ein schließendes Tag erkennt. Erkennt der Parser z.B. ein
einleitendes Tag, wird die Methode startElement mit dem Namen des Tags und
sämtlichen Attributwertpaaren als Parameter aufgerufen. In dieser Methode kann der
Programmierer nun die Verarbeitung des Tags steuern, z.B. Name und Attribute des
Tags ausgeben. Analog dazu wird die Methode endElement aufgerufen, wenn der
Parser ein schließendes Tag erkennt. Erkennt der Parser hingegen freien Text, der
zwischen Tags steht, wird die Methode characters aufgerufen, findet er eine
Processing Instruction die Methode processingInstruction. Außerdem gibt es noch die
Methode setDocumentLocator, mit der ein Locator-Objekt an den DocumentHandler
übergeben wird. Dieser Locator gibt an, wo der Parser sich gerade im XMLDokument befindet. Zur Behandlung von Whitespace gibt es die Methode
ignorableWhitespace.

ErrorHandler: Dieser Handler behandelt alle Fehler, die beim Parsen auftreten.
Dafür gibt es die Methoden warning, error und fatalError, die je nach Schwere des
Fehlers beim Parsen aufgerufen werden. Als Parameter erhalten diese Methoden eine
SAXParseException, in der nähere Angaben zur Art des Fehlers und der Position, an
der er aufgetreten ist, gespeichert sind.
Das SAX-API liefert zwar einen Default ErrorHandler in der HandlerBase Klasse,
dieser wirft aber nur Exceptions bei fatalErrors, andere Fehler ignoriert er.
-9-
Um Fehler in XML-Dokumenten zu erkennen, sollte man hier also auf jeden Fall
seinen eigenen ErrorHandler mit speziellen Fehlerbehandlungen schreiben und beim
Parser registrieren.

DTDHandler: Dieser Handler kommt zur Anwendung, wenn der Parser in einer DTD
auf ein unparsed entity, also Binärdaten mit einer zugehörigen Notation, trifft. Er ruft
dann entweder die Methode unparsedEntityDecl oder notationDecl auf, in der der
Programmierer seine eigene Behandlung der unparsed entities durchführen kann.

EntityResolver: Der EntityResolver wird benötigt, wenn der Parser auf Referenzen zu
externen Dateien, z.B. DTD's trifft, und diese lesen muß. Der Parser ruft dann die
einzige Methode dieses Handlers, resolveEntity auf, die aus einer öffentlichen
ID(URN=Universal Resource Name) eine System ID(URL=Universal Resource
Locator) macht.
3.3. Beispiel
Im Beispiel-Programm soll gezeigt werden, wie mit Hilfe des SAX-API der Inhalt einer
XML-Datei eine formatierte Ausgabe auf einem Medium generiert werden kann.
3.3.1. Funktion
Das Programm liest den Inhalt der XML-Datei1, der eine Mediensammlung repräsentieren
soll. Exemplarisch sind im Beispiel 2 CDs mit entsprechenden Titeln gefüllt.
Mit Hilfe der SAX - Bibliotheken wird der Inhalt formatiert auf einem Text-Ausgabemedium
ausgegeben (im Beispiel: JTextField).
Die eigentliche Aufgabe wird hier von der Call-Back Funktion "startElement(..)" gelöst. Sie
unterscheidet anhand des Tag-Namens, ob es sich bei dem gerade eingelesenen Tag um das
"CD" oder "Track" – Starttag handelt. Je nachdem werden die weiteren, in den Tag
Argumenten, enthaltenen Informationen entweder als CD-Überschrift oder die einzelnen
Daten formatiert als CD Track "Nr. Titel - Interpret (Zeit)" ausgegeben.
1
sh. Kapitel 6.1.
- 10 -
3.3.2. Quell-Code
Klasse DemoApplication:
package webservices_jaxp;
import javax.swing.UIManager;
import java.awt.*;
/**
* <p>Überschrift: WebServices - JAXP Eingabe</p>
* <p>Beschreibung: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Organisation: </p>
* @author Dominik Hanft
* @version 1.0
*/
public class DemoApplication_JAXP {
private boolean packFrame = false;
public DemoApplication_JAXP() {
frmMain frame = new frmMain();
if (packFrame) {
frame.pack();
}
else {
frame.validate();
}
/*
* Das Fenster zentrieren
*/
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
frame.setLocation((screenSize.width - frameSize.width) / 2,
(screenSize.height - frameSize.height) / 2);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {
e.printStackTrace();
}
new DemoApplication_JAXP();
}
}
- 11 -
Klasse Echo:
package webservices_jaxp;
import
import
import
import
import
java.io.*;
org.xml.sax.*;
org.xml.sax.helpers.DefaultHandler;
javax.xml.parsers.SAXParserFactory;
javax.xml.parsers.SAXParser;
/**
* <p>Überschrift: WebServices - JAXP SAX Eingabe</p>
* <p>Beschreibung: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Organisation: </p>
* @author Dominik Hanft
* @version 1.0
*/
public class Echo extends DefaultHandler{
static private javax.swing.JTextArea out;
/**
* Textarea als Ausgabemedium übernehmen
*/
public Echo(javax.swing.JTextArea myWriter) {
out = myWriter;
}
/**
* Gibt den übergeben String auf dem Ausgabemedium aus
* @param s auszugebener String
*/
private void emit(String s) throws SAXException {
try {
out.append(s);
out.repaint();
} catch (Exception e) {
throw new SAXException("Ausgabefehler!", e);
}
}
/**
* Gibt einen Zeilenumbruch auf dem Ausgabemedium aus
*/
private void nl() throws SAXException {
String lineEnd = System.getProperty("line.separator");
try {
out.append(lineEnd);
} catch (Exception e) {
throw new SAXException("I/O error", e);
}
}
- 12 -
/**
* Gibt einen Info-Text beim Start des Parsers aus
*/
public void startDocument() throws SAXException {
emit("<!-nl();
nl();
Start ... -->");
}
/**
* Gibt einen Info-Text beim Beenden des Parsers aus
*/
public void endDocument() throws SAXException {
emit("<!-- Fertig ! -->");
nl();
}
public void startElement(String namespaceURI,
String sName, // simple name
String qName, // qualified name
Attributes attrs)
throws SAXException
{
String eName = sName; // element name
if ("".equals(eName)) eName = qName; // not namespaceAware
if (eName=="CD") {
// CD Titel ausgeben
emit(attrs.getValue("Name"));
nl();
for (int i = 0; i < attrs.getValue("Name").length(); i++) {
emit("-");
}
nl();
} else if (eName=="Track") {
// einzelnen Track mit Länge ausgeben
emit(attrs.getValue("Nr") + ": ");
emit(attrs.getValue("Name") + " - " +
attrs.getValue("Interpret"));
emit(" ("+attrs.getValue("Länge")+")");
nl();
}
}
public void endElement(String namespaceURI,
String sName, // simple name
String qName // qualified name
)
throws SAXException
{
- 13 -
String eName = sName; // element name
if ("".equals(eName)) eName = qName; // not namespaceAware
if (eName=="CD") {
nl();
}
}
}
Klasse frmMain:
package webservices_jaxp;
import
import
import
import
import
import
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.io.*;
java.util.*;
org.xml.sax.*;
org.xml.sax.helpers.DefaultHandler;
javax.xml.parsers.SAXParserFactory;
javax.xml.parsers.ParserConfigurationException;
javax.xml.parsers.SAXParser;
/**
* <p>Überschrift: WebServices - JAXP Eingabe</p>
* <p>Beschreibung: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Organisation: </p>
* @author Dominik Hanft
* @version 1.0
*/
public class frmMain extends JFrame {
private JPanel contentPane;
static private Writer out;
private JPanel jPanel1 = new JPanel();
private JButton cmdStart = new JButton();
private JButton cmdExit = new JButton();
private BorderLayout borderLayout1 = new BorderLayout();
private JPanel jPanel2 = new JPanel();
private BorderLayout borderLayout2 = new BorderLayout();
private JScrollPane jScrollPane1 = new JScrollPane();
private JTextArea taAusgabe = new JTextArea();
private JButton cmdClear = new JButton();
private JPanel jPanel3 = new JPanel();
private JTextField tfFileName = new JTextField();
private JButton jButton1 = new JButton();
public frmMain() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
- 14 -
private void jbInit() throws Exception
{
//setIconImage(Toolkit.getDefaultToolkit().createImage(frmMain.class.getRes
ource("[Ihr Symbol]")));
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(600, 500));
this.setTitle("JAXP Demo");
cmdStart.setText("Start Parser");
cmdStart.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdStart_actionPerformed(e);
}
});
cmdExit.setText("Ende");
cmdExit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdExit_actionPerformed(e);
}
});
jPanel2.setLayout(borderLayout2);
cmdClear.setText("Clear");
cmdClear.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdClear_actionPerformed(e);
}
});
tfFileName.setFont(new java.awt.Font("Dialog", 0, 12));
tfFileName.setPreferredSize(new Dimension(400, 24));
tfFileName.setText("C:\\Dokumente und
Einstellungen\\dhanft\\jbproject\\WebServices_JAXP\\beispiel.xml");
tfFileName.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
tfFileName_actionPerformed(e);
}
});
jButton1.setText("Browse");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
contentPane.add(jPanel1, BorderLayout.NORTH);
jPanel1.add(cmdStart, null);
jPanel1.add(cmdClear, null);
jPanel1.add(cmdExit, null);
contentPane.add(jPanel2, BorderLayout.CENTER);
jPanel2.add(jScrollPane1, BorderLayout.CENTER);
contentPane.add(jPanel3, BorderLayout.SOUTH);
jPanel3.add(tfFileName, null);
jPanel3.add(jButton1, null);
jScrollPane1.getViewport().add(taAusgabe, null);
}
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
- 15 -
private void echoStart() throws Exception {
// Instanz der eigenen Klasse als SAX event handler erzeugen
DefaultHandler myHandler = new webservices_jaxp.Echo(taAusgabe);
// Instanz der SAX Factory erzeugen
SAXParserFactory myFactory = SAXParserFactory.newInstance();
// Instance des SAX Parsers von der Factory holen
SAXParser saxParser = myFactory.newSAXParser();
try {
// XML Datei parsen
saxParser.parse( new File(tfFileName.getText()), myHandler );
} catch (Throwable t) {
t.printStackTrace();
}
}
void cmdStart_actionPerformed(ActionEvent e) {
try {
echoStart();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
void cmdExit_actionPerformed(ActionEvent e) {
System.exit(0);
}
void cmdClear_actionPerformed(ActionEvent e) {
taAusgabe.setText("");
}
void jButton1_actionPerformed(ActionEvent e) {
java.awt.FileDialog myFileChooser = new
java.awt.FileDialog(this,"XML File auswählen",1);
myFileChooser.setMode(java.awt.FileDialog.LOAD);
myFileChooser.show();
tfFileName.setText(myFileChooser.getDirectory() +
myFileChooser.getFile());
}
void tfFileName_actionPerformed(ActionEvent e) {
}
}
- 16 -
4. DOM
Das Document Object Model (DOM) ist eine allgemeine Beschreibung eines Application
Programming Interface (API) für HTML und XML Dokumente. Es beschreibt die innere
Struktur von Dokumenten und die Art und Weise wie auf solche Dokumente zugegriffen
werden kann, bzw. wie diese bearbeitet werden.
Programmierer können mit Hilfe des DOM komplette Dokumente erstellen bzw. deren
Struktur verändern indem einzelne Elemente hinzugefügt, entfernt oder bearbeitet werden.
Damit das DOM auf vielen verschiedenen Plattformen eingesetzt werden kann, wurde die
Spezifikation in der plattformunabhängigen Sparche IDL der Object Management Groupe
(OMG) verfasst. (IDL = Interace Definition Language). IDL wird in der CORBA
Spezifikation beschrieben. Das DOM ist daher nur eine Vorlage, die erst für verschiedene
Programmierumgebungen implementiert werden muß. Für einige Sprachen wie z.B. Java,
C++, ECMAScript und Perl sind DOM Impementierungen frei verfügbar.
Geschichte
Das DOM wurde und wird vom World Wide Web Consortium (W3C) entwickelt. Der
Anfang wurde im September 1997 mit einem Dokument (DOM requirements document)
gemacht, welches prinzipiell die Anforderungen an eine solche API festhält. Von diesen
Vereinbarungen ausgehend entstand das DOM Level 1, welches im Oktober 1998 in der
ersten Version fertig wurde. Im Dezember des gleichen Jahres veröffentlichte das W3C das
erste ``public draft'' der DOM Level 2 Spezifikation. Laut W3C wird DOM
Level 2 in kürze vollendet sein.
Im Mai 2000 entstand eine erweiterte Version des DOM requirements document. Dieses
bildet die Grundlage für die nächste DOM Generation, DOM Level 3. Ein ``public draft''
hat das W3C bereits im September 2000 veröffentlicht.
Die einzelnen Levels der DOM Spezifikation sind untereinander abwärtskompatibel. Sie
unterscheiden sich nur in der Größe des Funktionumfanges.
Was ist DOM?
Die Abkürzung DOM steht für Document Object Model. Eine Übersetzung ins deutsche
bedeutet soviel wie Objektorientiertes Dokumentenmodell. Unter der Abkürzung DOM
verbergen sich jedoch zwei Bedeutungen. Spricht man von diesem so muß unterschieden
werden welche der beiden Bedeutungen gerade gemeint ist. Es ist zum einen ein
Objektorientiertes Modell zur logischen Darstellung von Informationen aller Art, und zum
anderen eine Schnittstellendefinition die es erlaubt in einem solchen Modell sich zu bewegen,
Informationen aus dem Modell zu lesen und dieses Modell zu manipulieren. Unter
Manipulation versteht man das Hinzufügen, Verändern oder Entfernen von Informationen aus
einem solchen Modell.
- 17 -
4.1. Funktionsweise
Das DOM definiert in erster Linie eine logische Struktur für XML - Dokumente. Im
allgemeinen wird jede Form von Daten, die mit Hilfe von XML strukturiert werden kann als
Dokument im Sinne des DOM angesehen. Das bedeutet dass auch HTML - Dokumente
gültige Dokumente im Sinne des DOM sind, da HTML durch eine passende XML Sprachdefinition abgebildet werden kann. Welche Informationen und wie man Informationen
aller Art in XML strukturieren kann und damit als ein DOM ansehen kann soll der Abschnitt
Daten in "DOM - Form" verdeutlichen.
4.2. Spezifikation
Es wird festgelegt, wie ein DOM Client mit einer DOM-Implementierung über Interfaces
kommuniziert.
- 18 -
4.3. Beispiel
Im Beispiel-Programm soll gezeigt werden, wie ein DOM Tree aus einem XML-Dokument
aufgebaut und ein XML-Dokument aus einem DOM Tree generiert werden kann.
4.3.1. Funktion
Zuerst wird ein Fenster mit zwei "Textfeldern" erzeugt. Über einen entsprechenden Button
kann man eine beliebige XML-Datei2 auswählen, die dann im linken Textfeld erscheint. Mit
dem Button "Transformieren" (in der Mitte des Fensters), wird der Inhalt der zuvor
ausgewählten Datei in ein temporäres File zwischengespeichert und die Methode Parse()
der Klasse "JAXPDOM" aufgerufen. Als Parameter werden boolsche Werte übergeben,
welche die Flags zur Einstellung des Parsers repräsentieren.
Die Datei "temp.dat" wird nun geparst und ein DOM-Tree aufgebaut.
Mit Hilfe der Methode "knotenAusgeben()" wird der Baum, beginnend mit seinem Wurzelknoten grafisch zur Ausgabe gebracht, und zwar im rechten Feld, in Form eines JTrees aus
der Swing-Bibliothek. Über diesem Feld befindet sich ein weiterer Button, welcher mit Hilfe
eines XSLT-Wandlers den DOM-Tree zurück in ein XML-Dokument transformiert.
Der Benutzer kann für dieses Dokument einen beliebigen Namen auswählen.
4.3.2. Quell-Code
///////////////////////////////////////////////////////////////////////////////////////
// Beispiel für eine JAVA-Anwendung mit JAXP.
//
// Beinhaltet:
//
// - DOM Tree aus einem XML-Dokument aufbauen
//
// - XML-Dokument aus einem DOM Tree generieren //
// im Rahmen des Gruppenreferats zum Thema JAXP
//
// Autor: Mark Vackiner
//
// Datum: 02.11.2002
//
// Dozent: Prof. Dr. Alois Schütte
//
///////////////////////////////////////////////////////////////////////////////////////
import java.awt.Frame;
import java.awt.*;
import java.awt.event.*;
import java.awt.GridBagLayout.*;
import java.io.*;
2
sh. Kapitel 6.2.
- 19 -
import javax.swing.*;
import javax.swing.tree.*;
// Imports fuer JAXP
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.w3c.dom.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
public class JAXP_mit_DOM extends Frame
{
// Klassenweit gueltige Variablen
private TextField tf_datei;
private TextArea ta_links;
private JTree ta_rechts;
private JAXPDOM jaxpdom;
private Checkbox box1, box2, box3, box4, box5, box6;
private Frame myFrame;
public static void main(String args[])
{
JAXP_mit_DOM beispiel = new JAXP_mit_DOM("JAXP mit DOM");
beispiel.setSize(800,600);
beispiel.show();
}
public JAXP_mit_DOM(String Titel)
{
super(Titel);
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent event)
{
dispose();
System.exit(0);
}
});
myFrame = this;
// Baum aus der Swing-Bibliothek mit Wurzelknoten 'root' erzeugen,
// mit ScrollPane versehen und dem Panel hinzufuegen
DefaultMutableTreeNode root;
root = new DefaultMutableTreeNode("Document");
ta_rechts = new JTree(root);
- 20 -
Panel cp = new Panel();
cp.setLayout(new BorderLayout());
cp.add(new JScrollPane(ta_rechts), BorderLayout.CENTER);
cp.setSize(new Dimension(200,300));
// AWT-Elemente fuer die GUI erzeugen
tf_datei = new TextField(25);
ta_links=new TextArea(33,45);
Panel panel = new Panel();
Label label = new Label("Pfad und Name der Datei:");
Button b_laden = new Button("Laden");
Button b_mitte=new Button("Transformieren >>");
Button b_speichern = new Button("Als XML-Dokument speichern");
tf_datei.setText("");
Panel p_unten = new Panel();
// Checkboxen erzeugen und auf unteres Panel platzieren
box1 = new Checkbox("Validating");
box2 = new Checkbox("NamespaceAware");
box3 = new Checkbox("IgnoringComments");
box4 = new Checkbox("IgnoringWhitespace");
box5 = new Checkbox("Coalescing");
box6 = new Checkbox("ExpandEntityReferences");
p_unten.add(box1);
p_unten.add(box2);
p_unten.add(box3);
p_unten.add(box4);
p_unten.add(box5);
p_unten.add(box6);
// Objekt der Klasse JAXPDOM erzeugen, in der JAXP implementiert ist
jaxpdom = new JAXPDOM(ta_rechts, root);
// Elemente zur Dateiauswahl auf Panel abbilden
panel.add(label);
panel.add(tf_datei);
panel.add(b_laden);
// Hintergrundfarbe einstellen (freundliches Grau)
setBackground(new Color(236,233,215));
// Layoutmanager vom Typ GridBagLayout mit Eigenschaften erzeugen
// und auf Hauptfenster anwenden
GridBagLayout layout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
setLayout(layout);
// Platzierung Panel fuer Dateihandling
constraints.gridy = 0;
- 21 -
constraints.gridx = 0;
constraints.gridheight = 1;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(2, 2, 2, 2);
layout.setConstraints(panel, constraints);
add(panel);
// Platzierung Button zum Transformieren und Speichern einer Datei
constraints.gridy = 0;
constraints.gridx = 2;
constraints.gridheight = 1;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(2, 2, 2, 2);
constraints.gridwidth = GridBagConstraints.REMAINDER;
layout.setConstraints(b_speichern, constraints);
add(b_speichern);
// Platzierung TextArea ta_links zur Darstellung von Dateiinhalten
constraints.gridy = 1;
constraints.gridx = 0;
constraints.weightx = 1;
constraints.weighty = 1;
constraints.fill = GridBagConstraints.BOTH;
constraints.gridwidth = 1;
layout.setConstraints(ta_links,constraints);
add(ta_links);
// Platzierung Button b_mitte
constraints.gridy = 1;
constraints.gridx = 1;
constraints.weightx = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.gridwidth = 1;
layout.setConstraints(b_mitte, constraints);
add(b_mitte);
// Platzierung JTree ta_rechts
constraints.gridy = 1;
constraints.gridx = 2;
constraints.weightx = 1;
constraints.fill = GridBagConstraints.BOTH;
constraints.gridwidth = 1;
layout.setConstraints(cp,constraints);
add(cp);
// Platzierung Panel p_unten
constraints.gridheight = 1;
constraints.gridy = 2;
constraints.gridx = 0;
constraints.weighty = 0;
constraints.gridwidth = GridBagConstraints.REMAINDER;
- 22 -
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.WEST;
layout.setConstraints(p_unten,constraints);
add(p_unten);
// ActionListener fuer Button b_laden
/////////////////////////////////////////////////////////////
b_laden.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// Fenster 'dlgLaden' zum Auswaehlen einer Datei erzeugen und
// auf dem Bildschirm anzeigen
FileDialog dlgLaden =
new FileDialog(myFrame,"Datei laden",FileDialog.LOAD);
dlgLaden.show();
// Wenn nach Schliessen des Fensters keine Datei ausgewaehlt
// wurde, abbrechen, sonst Pfad und Dateiname in Textfeld
// 'tf_datei' einlesen
if (dlgLaden.getFile() != null)
{
tf_datei.setText(new String
(dlgLaden.getDirectory()+dlgLaden.getFile()));
// Der Inhalt der zuvor ausgewaehlten Datei wird ausgelesen und
// im linken Textfeld 'ta_links' zur Anzeige gebracht
try
{
FileInputStream lesen =
new FileInputStream(tf_datei.getText());
BufferedInputStream temp = new BufferedInputStream(lesen);
byte inhalt[] = new byte[4096];
while (temp.available() > 0)
{
temp.read(inhalt);
}
ta_links.setText(new String(inhalt));
}
catch (IOException ex)
{
System.err.println(ex.toString());
}
}
}
});
- 23 -
// ActionListener fuer Button b_speichern
/////////////////////////////////////////////////////////////
b_speichern.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// Fenster 'dlgLaden' zum Auswaehlen einer Datei, diese
// erzeugen und auf dem Bildschirm anzeigen
FileDialog dlgSpeichern =
new FileDialog(myFrame, "Speichern unter", FileDialog.SAVE);
dlgSpeichern.show();
// Wenn ein Name fuer die zu speichernde Datei
// ausgewaehlt wurde, wird ein Objekt von der Klasse "File"
// erzeugt und der Methode 'transform' der Klasse 'JAXPDOM'
// als Parameter übergeben
if (dlgSpeichern.getFile() != null)
{
File f = new File (dlgSpeichern.getFile());
jaxpdom.transform(f);
}
}
});
// ActionListener fuer Button b_mitte
////////////////////////////////////////////////////////////
b_mitte.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// Es wird versucht, eine temporaere Datei mit Namen 'temp.dat'
// in das aktuelle Verzeichnis zu schreiben. In diese Datei
// soll der Inhalt der linken Text-Area geschrieben werden
try
{
FileOutputStream out = new FileOutputStream("temp.dat");
byte[] zeichen = ta_links.getText().getBytes();
out.write(zeichen);
}
catch (IOException ey)
{
ey.printStackTrace();
}
// Die Methode 'Parse' der Klasse 'JAXPDOM' wird aufgerufen
// Die boolschen Werte aus den Checkboxen werden als Parameter
// uebergeben
jaxpdom.Parse(box1.getState(),
box2.getState(), box3.getState(), box4.getState(),
box5.getState(), box6.getState());
}
});
- 24 -
}
}
//////////////////////////// Klasse JAXPDOM ///////////////////////////////
class JAXPDOM
{
// Klassenweit gueltige Variablen
private JTree tree;
private DefaultMutableTreeNode root;
private DefaultTreeModel model;
private Document doc;
public JAXPDOM(JTree ta_rechts, DefaultMutableTreeNode r)
{
// Klassenweit gueltige Variablen belegen
tree = ta_rechts;
root = r;
model = (DefaultTreeModel)tree.getModel();
}
public void Parse (boolean val, boolean nsa, boolean ic, boolean iecw,
boolean coal, boolean eer)
{
// Flags setzen
DocumentBuilder parser;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(val);
factory.setNamespaceAware(nsa);
factory.setIgnoringComments(ic);
factory.setIgnoringElementContentWhitespace(iecw);
factory.setCoalescing(coal);
factory.setExpandEntityReferences(eer);
// Die temporäre Datei "tmp.dat" parsen, das Baumdiagramm bis auf den
// Wurzelknoten löschen, neu aufbauen und die Anzeige aktualisieren
try
{
parser = factory.newDocumentBuilder();
doc = parser.parse(new File("temp.dat"));
root.removeAllChildren();
knotenAusgeben(doc,root);
model.reload();
}
// Ausnahmebehandlung
catch (SAXException e)
{
System.out.println("Fehler: " + e);
}
catch (IOException e)
{
System.out.println("I/O-Fehler: " + e);
- 25 -
}
catch (ParserConfigurationException e)
{
System.out.println("Fehler beim Laden d. Parser-Implementation: " + e);
}
}
private void knotenAusgeben(Node knoten, DefaultMutableTreeNode parent)
{
// Die Methode "knotenAusgeben()" durchlaeft den zuvor
// erzeugten DOM-Tree, beginnened mit der Wurzel, indem sie sich
// selbst aufruft, wenn ein Knoten weitere "Kinder" besitzt.
// Switch-Case unterscheidet den Knotentyp und baut dementsprechend
// einen Grafischen Baum der Klasse "JTree" auf.
// Die Namen der nachfolgenden Knotentypen sind weitestgehend selbst// erklaerend. Die Verarbeitung im Falle einer Verarbeitungsanweisung
// ("PROCESSING_INSTRUCTION_NODE") bleibt zur Vereinfachung
unberueck// sichtigt.
switch (knoten.getNodeType())
{
// Der Wurzelknoten ist das Dokument selbst. Die Wurzel existiert
// bereits im JTree, deshalb wird nur nach "Kind-Knoten" gesucht.
case Node.DOCUMENT_NODE:
DefaultMutableTreeNode child;
Document doc=(Document)knoten;
knotenAusgeben(doc.getDocumentElement(),root);
break;
// Ein "ganz normaler" Knoten (XML-Element)
case Node.ELEMENT_NODE:
String name = knoten.getNodeName();
// Suche nach Attributen
NamedNodeMap attribute = knoten.getAttributes();
if (attribute.getLength() != 0)
{
for (int i=0; i<attribute.getLength(); i++)
{
Node aktuell = attribute.item(i);
name = name + " " + aktuell.getNodeName() +
"=" + aktuell.getNodeValue();
}
}
child = new DefaultMutableTreeNode(name);
model.insertNodeInto(child, parent, 0);
NodeList kinder = knoten.getChildNodes();
if (kinder != null)
{
- 26 -
for (int i=0; i<kinder.getLength(); i++)
{
knotenAusgeben(kinder.item(i), child);
}
}
break;
// Fuer Text- bzw. "CDATA"-Knoten erolgt die gleiche Visualisierung
// im JTree
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
child = new DefaultMutableTreeNode("T: "
+ knoten.getNodeValue());
model.insertNodeInto(child, parent, 0);
break;
// Dieser Knotentyp wird ignoriert (siehe Text oben)
case Node.PROCESSING_INSTRUCTION_NODE:
// Verarbeitungsanweisungen ausgeben
break;
// Mit Hilfe einer Entity-Referenz kann man eine Zeichenkombination
// in einem XML-Dokument durch eine beliebige andere Zeichenkette
// ersetzen, z.B. auch Sonderzeichen
case Node.ENTITY_REFERENCE_NODE:
child=new DefaultMutableTreeNode("ER: " +
knoten.getNodeValue());
model.insertNodeInto(child, parent, 0);
break;
// Dieser Knoten beinhaltet die DTD-Information
case Node.DOCUMENT_TYPE_NODE:
DocumentType docType = (DocumentType)knoten;
String ausgabe = "DT: " + docType.getName();
if (docType.getPublicId() != null)
{
ausgabe = ausgabe + " PUBLIC \"" + docType.getPublicId();
}
else
{
ausgabe = ausgabe + " SYSTEM ";
}
child=new DefaultMutableTreeNode(ausgabe);
model.insertNodeInto(child, parent, 0);
break;
}
}
public void transform (File f)
- 27 -
{
// Hier werden werden jeweils ein Objekt der TransformerFactory,
// und des Transformers, der die Implementierung der XSLT-Wandlers
// beinhaltet. Ziel und Quelle werden für das anschliessende Parsen
// bestimmt
try
{
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(f);
transformer.transform(source, result);
}
// Ausnahmebehandlung
catch (TransformerConfigurationException tce)
{
// Fehler des Parsers
System.out.println ("* Transformer Factory error");
System.out.println(" " + tce.getMessage() );
// Contained Exception
Throwable x = tce;
if (tce.getException() != null)
x = tce.getException();
x.printStackTrace();
}
catch (TransformerException te)
{
// Fehler, die durch den Parser hervorgerufen werden
System.out.println ("* Transformation error");
System.out.println(" " + te.getMessage() );
}
}
}
- 28 -
5. XSLT
Die Abkürzung XSLT steht für eXtensible Stylesheet Language Transformations.
Dahinter verbirgt sich eine Sprache, die entworfen wurde, um vorzugsweise XMLDokumente in andere XML-Dokumente zu transformieren. XSLT ist ein Bestandteil von
XSL.
Transformation bedeutet das Überführen einer Menge von Informationen durch Anwenden
von Regeln in eine andere Darstellungsform. Will man nicht die gesamte Menge an
Informationen transformieren, muss man vorher eine Selektion durchführen. Mit XPath kann
man eine Untermenge von Knoten aus einem Dokument selektieren, weshalb XPath im
Zusammenhang mit XSLT eine große Bedeutung zukommt
Bei einer Transformation geht in aller Regel Information verloren, es könnte aber auch
neue Information hinzukommen. Eine Transformation ist also nicht zwingend umkehrbar.
Grundsätzliche Arbeitsweise
Der XSLT-Prozess benötig zwei Dokumente:
1. das zu transformierende Quelldokument, das ein XML-Dokument sein
muss und
2. ein Stylesheet, das auch im XML-Format vorliegt und in dem sich die
Transformationsvorschriften befinden.
Als Ergebnis erhalten wir einen Ausgabedatenstrom, der das transformierte Dokument
beinhaltet. Dieses Dokument kann, muss aber nicht zwangsläufig ein XML-Dokument sein.
Der Transformationsprozess arbeitet nicht direkt auf den Dokumenten, sondern auf
einer DOM-Repräsentation, also einem Baum, in dem die einzelnen Elemente des
Quelldokuments und auch des Stylesheets durch Knoten repräsentiert werden.
Der Transformationsprozess traversiert das Quelldokument und beginnt dabei an der
Dokumentwurzel. Jetzt wird im Stylesheet eine passende Regel gesucht, die beschreibt,
was mit diesem Knoten geschehen soll. Diese Regel wird Template Rule genannt. Findet
der XSLT-Prozessor eine Template Rule, die für die Dokumentwurzel zuständig ist, wird
sie instanziiert. Das instanziieren der Template Rule ist das traversieren des Teilbaums im
Stylesheet, der den Inhalt der Template Rule bildet.
Jetzt wird für jedes Element in diesem Stylesheet-Fragment überprüft, ob es zur
Anwendungsdomäne des XSLT-Prozessors gehört. Gehört das Element dazu, wird es
ausgewertet. Gehört es zu einem Namensraum, der für Erweiterungen innerhalb des
- 29 -
Stylesheets deklariert wurde, wird es an einen näher zu spezifizierenden
Auswertungsprozess weitergegeben. Besitzt das Element keinen Namensraum oder ist
dieser für den Prozessor nicht von Bedeutung, wird das Element nach erfolgter
Attributexpansion in das Ergebnisdokument geschrieben.
Innerhalb der Template Rule wird jetzt die Auswahl von Elementen aus dem
Quelldokument gesteuert. Hierzu kann man sich verschiedener XSLT-Konstrukte bedienen,
z.B. xsl:apply-templates oder xsl:for-each. Im select-Argument von xsl:apply-templates wird ein
XPath-Audruck angegeben, der Elemente aus dem Quelldokument auswählt. Auf diese
Elemente sollen dann wieder Template Rules angewendet werden.
Man erkennt schon an dieser Stelle, dass wir es mit einem ständigen Wechsel zwischen
Stylesheet und Quelldokument zu tun haben.
Wird keine passende Template Rule gefunden, kommen so genannte implizite Regeln
(Built-in Template Rules) zum Einsatz. Diese Regeln spezifizieren das Verhalten für alle
unterschiedliche Knotentypen, falls keine expliziten Regeln im Stylesheet gefunden
werden.
Folgende Grafik möge den Transformationsprozess noch einmal verdeutlichen:
Die grundsätzliche Arbeitsweise von XSLT
- 30 -
6. Anhang
6.1. SAX
Datei “test.dtd” :
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT Mediensammlung (((Track)))>
Datei „test.xml“ :
<?xml version="1.0" encoding="UTF-8"?>
<Mediensammlung Name="Meine Sammlung">
<Audio Name="Audio - Archiv ( CDs , LPs , MP3s )">
<CD Gesamtlänge="73:15" Producer="BMG" Name="Kuschelrock 16">
<Track Länge="3:10" Interpret="Shakira" Name="Underneath Your Clothes" Nr="1"/>
<Track Länge="2:95" Interpret="Lenny Kravitz" Name="Stillness Of Heart" Nr="2"/>
<Track Länge="3:20" Interpret="Faith Hill" Name="There You'll Be" Nr="3"/>
<Track Länge="3:05" Interpret="Robbie Williams" Name="Better Man" Nr="4"/>
<Track Länge="2:90" Interpret="Kate Winslet" Name="What If" Nr="5"/>
<Track Länge="2:16" Interpret="Alicia Keys" Name="Fallin'" Nr="6"/>
</CD>
<CD Gesamtlänge="73:15" Producer="BMG" Name="CD2">
<Track Länge="3:10" Interpret="Interpret" Name="track 1" Nr="1"/>
<Track Länge="2:95" Interpret="Interpret" Name="track 2" Nr="2"/>
<Track Länge="3:20" Interpret="Interpret" Name="track 3" Nr="3"/>
<Track Länge="3:25" Interpret="Interpret" Name="track 4" Nr="4"/>
<Track Länge="2:15" Interpret="Interpret" Name="track 5" Nr="5"/>
<Track Länge="2:80" Interpret="Interpret" Name="track 6" Nr="6"/>
</CD>
</Audio>
</Mediensammlung>
6.2. DOM
Datei „tontraeger.dtd“ :
<?xml version="1.0" encoding="UTF-8"?>
<!--Das ist die Dokument-Type-Definition fuer cd.xml-->
<!ELEMENT tontraeger (cd)>
<!ELEMENT cd (lied*)>
<!ATTLIST cd
interpret CDATA #REQUIRED
titel CDATA #REQUIRED
>
<!ELEMENT lied (#PCDATA)>
<!ATTLIST lied
name
CDATA #REQUIRED
laenge CDATA #REQUIRED
>
- 31 -
XML-Datei :
<?xml version="1.0"?>
<!DOCTYPE tontraeger SYSTEM "tontraeger.dtd">
<tontraeger>
<cd interpret="David Bowie" titel="LOW">
<lied name="Speed of life" laenge="2:46">...</lied>
<lied name="Breaking glass" laenge="1:51">...</lied>
<lied name="What in the World" laenge="2:23">...</lied>
<lied name="Sound & vision" laenge="3:03">...</lied>
<lied name="Always crashing in the same car" laenge="3:29">...</lied>
<lied name="Be my wife" laenge="2:55">...</lied>
<lied name="A new Career in a new Town" laenge="2:51">...</lied>
<lied name="Warzawa" laenge="6:20">...</lied>
<lied name="Art decade" laenge="3:43">...</lied>
<lied name="Weeping wall" laenge="3:26">...</lied>
<lied name="subterraneans" laenge="5:39">©</lied>
</cd>
</tontraeger>
- 32 -
Herunterladen