Einführung in XML WS 2014/15 - KOBRA

Werbung
Lutz Wegner
Einführung in
XML
Universität
Kassel
<?xml version=“1.0“?>
<purchaseOrder orderDate=“1999-10-20“>
<shipTo country=“US“>
<name>Alice Smith</name>
<street>123 Maple Street</street>
<city>Mill Valley</city>
<zip>90952</zip>
</shipTo>
<items>
<item partNum=“872-AA“>
<productName>Lawnmower</productName>
<quantity>1</quantity>
<price>148.95</price>
</item>
</items>
</purchaseOrder>
SKRIPTEN DER
PRAKTISCHEN INFORMATIK
Einführung in XML
Lutz Wegner
Einführung in XML
Skriptum zur gleichnamigen Vorlesung
an der Universität Kassel
unter Mitarbeit von
Morad Ahmad und Kai Schweinsberg
Auflage Oktober 2014
Skriptum zur gleichnamigen Vorlesung an der Universität Kassel im
Wintersemester 2014/2015.
Prof. Dr. Lutz Wegner
Universität Kassel
FB 16 – Elektrotechnik/Informatik
Wilhelmshöher Allee 73
D-34109 Kassel
[email protected]
vii
Vorwort
Vorwort zur Auflage 2005
In dieser zweistündigen Vorlesung (mit zwei SWS Übung) sollen die
Grundlagen der eXtensible Markup Language, die sich als Datenaustauschsprache etabliert hat, erläutert werden. Im Gegensatz zu HTML
erlaubt XML die semantische Anreicherung von Dokumenten. Neben
XML sollen auch die eXtensible Stylesheet Language (XSL), XML
Schema, die DOM-Schnittstelle, XPath, XQuery und andere Komponenten besprochen werden.
Das vorliegende Skript wurde erstmals zum WS 2004/05 in dieser
Form herausgegeben. Änderungen können im Laufe der Veranstaltung
noch einfließen. Als Grundlage diente mein älteres Skript aus dem Jahre
2000 und die Überarbeitung und Erweiterung von Dr. Morad Ahmad aus
dem WS 2003/04.
Da XML eine relativ neue Technologie darstellt und die Standards z.
T. noch in Entwicklung sind, bzw. täglich neue Software-Werkzeuge
angeboten werden, bin ich für Hinweise und die Mitwirkungen der Studenten bei der Fehlersuche dankbar. Alle Mängel gehen zu meinen
Lasten.
Kassel, im Oktober 2005
Lutz Wegner
ix
Inhalt
1
Einführung
1
1.1
Begriffe und Definitionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2
1.2
Warum XML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3
Entwicklung von Dokumenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4
1.4
Publizieren von XML-Dokumenten . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5
Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.5.1 Ein einfaches Stylesheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5.2 Eine einfache DTD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6
Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
2
Grundlagen
2.1
Aufbau von XML-Dokumenten . . . . . . . . . . . . . . . . . . . . . . . . . . . .15
15
2.1.1 Die XML-Deklaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.2 Die Dokumenttyp-Deklaration . . . . . . . . . . . . . . . . . . . . . . . .16
2.1.3 Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
2.1.4 Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19
2.1.5 Namensräume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.1.6 Wohlgeformte XML-Dokumente . . . . . . . . . . . . . . . . . . . . . . 22
2.1.7 Prozessor-Instruktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23
2.1.8 Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Einführung in XML
x
2.1.9 Kommentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.1.10 CDATA-Abschnitte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3
Document Type Definitions
27
3.1
Elementdeklarationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.1.1 ANY und #PCDATA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.1.2 Mehrfache Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.1.3 Gruppierung und Wiederholung . . . . . . . . . . . . . . . . . . . . . . 28
3.1.4 Leere Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.2
Entities und Notationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.2.1 Parameter-Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2.2 Externe Parameter-Entities . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2.3 Externe Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.2.4 Notationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.2.5 Unparsed Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.2.6 Zusammenfassung Entities . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.3
Attributlisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.4
Modularisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.4.1 Importieren von Modulen . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.4.2 Bedingte Abschnitte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.4.3 Verwenden der internen Teilmenge . . . . . . . . . . . . . . . . . . . . 42
3.5
Tipps zum Erstellen von DTDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.6
Ein Datenbankbeispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4
XML Schema
4.1
Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
49
4.1.1 Deklaration von Elementen . . . . . . . . . . . . . . . . . . . . . . . . . . 52
xi
4.1.2 Attributdeklaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .53
4.1.3 Globale Elemente und Attribute . . . . . . . . . . . . . . . . . . . . . .54
4.2
Vordefinierte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.2.1 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .57
4.2.2 Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.2.3 Zeit und Datum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .58
4.3
Einfache Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3.1 Einschränkung des Wertebereichs . . . . . . . . . . . . . . . . . . . . .59
4.3.2 xsd:enumeration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.3.3 xsd:length, xsd:maxLength und xsd:minLength . . . . . . . . . . 60
4.3.4 xsd:pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.3.5 Erweiterung zu Listentypen . . . . . . . . . . . . . . . . . . . . . . . . . .61
4.3.6 Erweiterung durch Vereinigung . . . . . . . . . . . . . . . . . . . . . . . 61
4.4
Komplexe Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.4.1 Definition von einfachem Inhalt . . . . . . . . . . . . . . . . . . . . . .62
4.4.2 Definition von komplexem Inhalt . . . . . . . . . . . . . . . . . . . . .63
4.4.3 Erweiterung von komplexen Elementen . . . . . . . . . . . . . . . . 66
4.4.4 Gemischter Inhalt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.5 anyType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .68
4.4.6 Leerer Inhalt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .68
4.4.7 Referenzieren von Elementen und Attributen . . . . . . . . . . . .68
5
XSLT und XPath
71
5.1
XSLT als XML-Dokument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .71
5.1.1 Erstes Beispiel: Hallo Welt . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.2
XPath-Datenmodell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .73
Einführung in XML
xii
5.3
Funktionsprinzip von Stylesheets . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.4
Einfache XSLT-Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.4.1 xsl:template und xsl:apply-templates . . . . . . . . . . . . . . . . . . 77
5.4.2 xsl:value-of . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.5
Auswahl von Knoten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.5.1 Lokalisierungspfade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.5.2 Auflösung von Regelkollisionen . . . . . . . . . . . . . . . . . . . . . . 90
5.5.3 Standardregeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.6
Weitere XSLT-Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.6.1 xsl:element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.6.2 xsl:attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.6.3 xsl:text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.6.4 xsl:copy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.6.5 xsl:copy-of . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.6.6 xsl:comment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.6.7 xsl:processing-instruction . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.7
XSLT-Kontrollstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.7.1 xsl:if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.7.2 xsl:for-each . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
5.7.3 xsl:choose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
5.8
Benannte Templates, Variablen und Parameter . . . . . . . . . . . . . . . . 99
5.8.1 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
5.9
Ausgabemethode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
5.10 Ein vollständiges Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6
SVG Scalable Vector Graphics
109
xiii
6.1
Elementare Graphikelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
6.2
Elementare Textelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
6.3
Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
6.4
Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.4.1 Mausereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.4.2 Tastaturereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.4.3 Dokumentereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .124
6.4.4 Animationsereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .125
6.5
Einbindung von Audiodateien . . . . . . . . . . . . . . . . . . . . . . . . . . . .126
6.6
Schlussbemerkung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
7
XQuery
7.1
Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
129
7.1.1 Elementare Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
7.1.2 Kommentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .132
7.1.3 Das XQuery-Datenmodell . . . . . . . . . . . . . . . . . . . . . . . . . .132
7.1.4 Einfache Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
7.1.5 Lokalisierungspfade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .135
7.1.6 Navigationsachsen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .136
7.1.7 Knotentest und Knotentypen . . . . . . . . . . . . . . . . . . . . . . . . 138
7.1.8 Prädikate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .139
7.2
FLWOR-Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
7.2.1 for- und let-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
7.2.2 where-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .146
7.2.3 order by-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
7.2.4 return-Klausel und Element-Konstruktoren . . . . . . . . . . . . . 150
7.3
Die Positionsvariable at . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .151
xiv
Einführung in XML
7.4
Weitere Möglichkeiten von XQuery . . . . . . . . . . . . . . . . . . . . . . . 151
7.4.1 XQuery Conditional Expressions (If-then-else-Konstrukt) 152
7.4.2 Eingebaute Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
7.4.3 Weitere XQuery-Besonderheiten . . . . . . . . . . . . . . . . . . . . 153
7.5
Einschub XQueryX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
8
SQL/XML
8.1
XML und Datenbanken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
157
8.1.1 Was ist SQL/XML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
8.2
XML-Publikationsfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
8.2.1 Anwendungsbeispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
8.2.2 Default View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
8.2.3 Funktionen zur XML-Erzeugung . . . . . . . . . . . . . . . . . . . . 163
8.2.4 Document Access Definitions in DB2 Extender . . . . . . . . . 168
8.3
Die Funktionen XMLQuery und XMLCast . . . . . . . . . . . . . . . . . . 168
8.4
XML-Datentyp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
8.5
Prädikate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
8.6
XMLTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
9
XPointer und XLink
9.1
Das XLink-Konzept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
9.2
Einfache und erweiterte Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
9.3
Regeln für den Gebrauch der Attribute in XLink . . . . . . . . . . . . . 182
9.4
Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
9.5
Linkbase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
9.6
XPointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
175
9.6.1 Kurzform mit ID-Attribut . . . . . . . . . . . . . . . . . . . . . . . . . . 186
9.6.2 Kurzform mit schemabestimmtem ID-Element . . . . . . . . . 188
xv
9.6.3 Kurzform mit ID-Attribut und DTD-Vereinbarung . . . . . . . 189
9.6.4 Schemabasierte Pointer: Child Sequence . . . . . . . . . . . . . . .189
9.6.5 Elementauswahl über ID . . . . . . . . . . . . . . . . . . . . . . . . . . .191
9.6.6 ID kombiniert mit „Child Sequence“ . . . . . . . . . . . . . . . . . .191
10
Document Object Model
193
10.1 Erzeugen eines DOM-Baums mit dem DOM-Parser . . . . . . . . . . .193
10.2 Darstellung des Dokuments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
10.3 Beschreibung der Objekte und Methoden . . . . . . . . . . . . . . . . . . . . 196
10.3.1 DOMException . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .197
10.3.2 DOMImplementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .197
10.3.3 DocumentFragment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
10.3.4 Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .197
10.3.5 Node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
10.3.6 Attr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
10.3.7 Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .201
10.3.8 Text und Comment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .202
10.3.9 NodeList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .202
10.3.10NamedNodeMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
10.4 Beispiel: Serialisieren eines DOM-Baumes . . . . . . . . . . . . . . . . . . 203
10.5 Beispiel: Erzeugen eines DOM-Baumes . . . . . . . . . . . . . . . . . . . . . 206
11
SAX
211
11.1 ContentHandler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .213
11.2 ErrorHandler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
11.3 Beispiel: Counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .220
12
XML-RPC und SOAP
225
12.1 Der Standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Einführung in XML
xvi
12.1.1 Methodenaufrufe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
12.1.2 Rückgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
12.2 XmlRpc-Schnittstelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
12.2.1 Installieren und Verwenden der XmlRpc-Schnittstelle . . . . 230
12.3 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
12.3.1 HelloServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
12.3.2 HelloHandler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
12.3.3 HelloClient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
12.4 SOAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
12.4.1 Botschaftenstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
12.4.2 Der SOAP-Kopf (Header) . . . . . . . . . . . . . . . . . . . . . . . . . . 235
12.4.3 Der SOAP-Rumpf (Body) . . . . . . . . . . . . . . . . . . . . . . . . . . 236
12.4.4 Fehlerfälle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
12.4.5 Übertragungsprotokolle . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Literaturverzeichnis
239
1
1 Einführung
Die Entwicklung von XML [1] begann Ende der 90er Jahre. Hierzu
schlossen sich mehrere Organisationen und führende Unternehmen der
Softwareindustrie zusammen, um einen Standard für die Entwicklung von
Markup-Sprachen zu definieren. XML selbst und viele darauf aufbauende
Standards wurden vom World Wide Web Consortium (W3C)1 verabschiedet.
Bevor wir mit der Einführung in XML starten, sollten zuerst einige
weit verbreitete, falsche Vorstellungen über XML ausgeräumt werden.
XML ist keine Markup-Sprache, sondern eine Metabeschreibungssprache, d. h. eine Sprache für die Definition anderer Markup-Sprachen wie
etwa HTML. Dabei gilt XML als Vereinfachung der Standard Generalized Markup Language (SGML). Damit ist das zweite, häufig zu hörende
Missverständnis ausgeräumt: XML wird HTML nicht ersetzen. Vielmehr
ist das neuere XML-basierte XHTML eine „sauberere“ Version von
HTML.
Die Verwendung von XML betrifft zwei große Bereiche. Als erstes
lässt sich XML für die Entwicklung von unterschiedlichen Beschreibungssprachen einsetzen. Vorhandene Beispiele dafür sind z. B. HTML,
SVG [2] und MathML [3]. Der zweite Anwendungsbereich liegt in der
Definition von Austauschformaten zwischen verschiedenen Anwendungen in heterogenen Netzen. Hierzu dienen die Standards SOAP [4] [5]
und XML-RPCs [6].
1. Siehe http://www.w3.org.
Einführung in XML
2
1.1
Begriffe und Definitionen
In dieser Vorlesung geht es um XML selbst sowie einige verwandte Technologien. Damit die vielen Abkürzungen und neuen Begriffe keine zu
große Verwirrung stiften, folgt hier eine kurze Erläuterung der Terminologie:
■
■
■
■
Markup und Markup-Sprachen
,,Markups sind Informationen, die einem Dokument hinzugefügt werden, um auf bestimmte Weise dessen Bedeutungsinhalt zu erweitern,
indem es die einzelnen Teile kennzeichnet und festlegt, wie diese
zueinander in Beziehung stehen. Eine Markup-Sprache ist eine Menge
von Symbolen, die im Text des Dokuments plaziert werden können,
um einzelne Teile dieses Dokuments zu benennen und sie voneinander
abzugrenzen“ [7].
DTD und XML Schema
Neben der Einhaltung der Syntaxvorschriften der Markup-Sprache
selbst kann man verlangen, dass Dokumente eigendefinierten Strukturregeln genügen. Dazu wurde in XML zuerst die sog. Document Type
Definition (DTD) von SGML übernommen. Da DTDs einige Nachteile aufweisen (dazu später), wurde ein neuer XML-basierter Standard für die Modellierung eines Dokuments entwickelt: XML Schema
Language [8].
XSL
Die Extensible Stylesheet Language (XSL) [9] besteht aus drei Hauptteilen: 1. XSL Transformation Language (XSLT [10] [11]) dient zur
Definition von Transformationen von XML-Dokumenten in andere
XML-basierte Sprachen. 2. Formating Objects (fo) bildet eine ausgereifte Formatierungsprache, die in ihrer Funktion etwa PDF ähnelt. 3.
XPath [12] ist eine von XSLT verwendete Sprache für die Adressierung von Elementen innerhalb eines XML-Dokuments.
XLink
XML enthält keine festgelegten Elemente, d. h. auch keine speziellen
Link-Elemente wie etwa das A-Element in HTML. Die XML Linking
Language (XLink) ist eine Sprache zur Definition von Link-Elemen-
Kapitel 1 – Einführung
■
■
■
■
3
ten in einer Markup-Sprache. XLink verwendet dazu die beiden Sprachen XPath und XPointer.
XPointer
Die XML Pointer Language (XPointer [13]) ist der Teil von XLink zur
Identifizierung von beliebigen Teilen eines Dokuments. XPointer
basiert auf XPath und unterstützt Adressierungen bis in die internen
Strukturen von XML-Dokumenten hinein.
DOM
Das Document Object Model (DOM) ist eine baumartige interne Darstellung eines XML-Dokuments (oder auch anderer Dokumentarten).
Dabei definiert der DOM-Standard nicht, wie dies von einer Implementierung realisiert wird. Vielmehr wird eine DOM-Schnittstelle für
die Bearbeitung eines Dokuments festgelegt. DOM ist in sog. Levels
unterteilt. Der DOM-Level 1 [15] behandelt XML-Dokumente im allgemeinen. DOM-Level 2 [16] enthält weitere Teile für spezielle XMLDokumente, etwa HTML oder SVG. Mithilfe der DOM-Schnittstelle
können Anwendungen XML-Dokumente dynamisch verändern und
haben einen wahlfreien, nicht sequentiellen Zugriff auf den Inhalt
eines Dokuments.
XML-Parser
Ein XML-Parser ist ein Programm, das XML-Dokumente auf syntaktische Korrektheit überprüft. Ein sog. validierender XML-Parser validiert zusätzlich ein XML-Dokument anhand einer DTD oder eines
XML Schemas. Meistens stellen XML-Parser weitere Funktionen zur
Verfügung, z. B. das Lesen eines Dokuments als DOM-Objekt. Einige
bekannte XML-Parser sind XT [18] und Xerces [19]. Xerces kann
über Schnittstellen verschiedener Programmiersprachen angesprochen
werden.
Stylesheet-Prozessor
Ein Stylesheet-Prozessor ist ein Programm, dass XML-Dokumente
anhand der Angaben eines Stylesheets transformiert. Beispiel eines
Stylesheet-Prozessors ist Xalan [20].
Einführung in XML
4
1.2
Warum XML?
Natürlich stellt sich bei der Einführung einer neuen Technologie die Frage
nach dem Sinn und den mit der Einführung verbundenen Zielen. Zusammengefasst können für XML die folgenden Vorteile bzw. Ziele genannt
werden:
■
Durch XML können Auszeichnungssprachen anwendungsspezifisch,
d. h. maßgeschneidert für die Bedürfnisse einer Anwendung, entwickelt werden. Dabei kann einem Auszeichnungselement eine semantische Bedeutung zukommen.
■
XML-Dokumente müssen eindeutige Regeln und Strukturen einhalten,
sodass ihre Verarbeitung durch Anwendungsprogramme einfacher und
sicherer geschehen kann als z. B. die Verarbeitung von HTML-Dokumenten.
■
Inhalt und Darstellung eines Dokuments werden voneinander getrennt,
sodass unterschiedliche Darstellungen desselben Dokuments möglich
sind. Somit können z. B. mehrere geräteabhängige Visualisierungen
aus einem einzigen Dokument erzeugt werden.
XML-Dokumente sind einfache, aber wohl strukturierte Textdokumente. Damit sind sie sowohl von menschlichen Lesern als auch von
Anwendungsprogrammen einfach zu interpretieren und zu verarbeiten.
XML geht mit Fehlerüberprüfung strenger um als z. B. HTML. Erfüllen Dokumente einige der Grundvoraussetzungen an die Syntaxregeln
nicht, so muss ein XML-Parser das Dokument abweisen und eine entsprechende Fehlermeldung produzieren.
■
■
1.3
Entwicklung von Dokumenten
XML-Dokumente werden auf verschiedene Arten erzeugt. Sie können die
Ausgabe von Anwendungsprogrammen sein, sie können durch spezielle
grafische Editoren erzeugt werden1 oder durch einfache Texteditoren per
Hand eingetippt werden.
1. Siehe z. B. XMLSpy unter http://www.xmlspy.softjam.co.uk.
Kapitel 1 – Einführung
5
Da wir in dieser Vorlesung den Aufbau von XML-Dokumenten, Stylesheets, DTDs und Schematas erlernen wollen, bietet sich die Verwendung eines einfachen Texteditors an. (Sie haben bestimmt einen
Lieblingseditor!). Dies hat weiterhin den Vorteil, sich nicht auf eine
bestimmte Plattform oder Software festlegen zu müssen, auch wenn ein
reiner Texteditor bei der Entwicklung größerer XML-basierter Projekte
eher zu mühsam wäre.
In der Regel definiert man eine Sprache durch Festlegung auf Sprachregeln. Im nächsten Schritt kann man für Dokumente fordern, dass sie
gewissen Formvorschriften genügen. Das geschieht mittels einer DTD
oder eines XML Schemas. Zur Überprüfung eines Dokuments auf syntaktische Korrektheit bezüglich eines Schemas (zur sog. Validierung) wird
ein XML-Parser aufgerufen. Dieser gibt in der Regel bei Fehlern detaillierte Fehlermeldungen mit Zeilennummern aus. Diese Fehler können
dann mit einem Texteditor korrigiert werden, bis das Dokument fehlerfrei
ist. Bei der Entwicklung eines Stylesheets geht man ähnlich vor. Zum
Testen des Stylesheets kann ein Stylesheet-Prozessor verwendet werden,
der eine Ausgabe in der Zielsprache erzeugt.
1.4
Publizieren von XML-Dokumenten
Für das Publizieren von XML-Dokumenten lassen sich drei Strategien
verwenden:
■
Das XML-Dokument wird mit dem Stylesheet an einen Client übertragen. Der Client (Browser) übersetzt das XML-Dokument in die Zielsprache (z. B. HTML) und stellt das Dokument dar.
■
Das XML-Dokument wird dynamisch bei einer Anfrage auf der Serverseite mithilfe eines Stylesheet-Prozessors in die Zielsprache transformiert und an den Client übertragen.
Das XML-Dokument wird statisch transformiert. Der Client fordert
die transformierten Dokumente an.
■
Die dynamische Erzeugung von XML-Dokumenten auf der Serverseite
scheint sich durchzusetzen, da diese die meisten Vorteile von XML vereint. Es werden damit keine Voraussetzungen an den Client gestellt. Wei-
Einführung in XML
6
terhin können vom Server verschiedene Ausgaben produziert werden,
welche die Leistungsfähigkeit des jeweiligen Clients berücksichtigen.
Im einfachsten Fall kann man dazu CGI-Skripte verwenden. In der
Praxis nutzt man oft PHP für diesen Zweck. Es existieren aber auch spezielle Umgebungen, die diese Aufgabe übernehmen. Sehr bekannt in diesem Bereich ist Cocoon von Apache. Falls es die Zeit zulässt, werden wir
uns am Ende dieser Vorlesung kurz mit Cocoon beschäftigen.1
1.5
Beispiel
Um den Prozess der Entwicklung von XML-Dokumenten zu demonstrieren, betrachten wir zuerst ein einfaches Beispiel. Dabei gehen wir hier
nicht auf die Details ein, die wir dann in den folgenden Kapiteln behandeln werden.
Programm 1–1 Teilnehmerliste („teilnehmer0.xml“)
<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE TeilnehmerS SYSTEM "teilnehmer0.dtd">
<TeilnehmerS vorlesung='Einführung in XML'>
<Teilnehmer matrNr='4711'>
<name>Möller</name>
<vorname>Alex</vorname>
<semester>6</semester>
<fachb>16</fachb>
</Teilnehmer>
<Teilnehmer matrNr="4722">
<name>Schmidt</name>
<vorname>Hans</vorname>
<semester>8</semester>
<fachb>17</fachb>
</Teilnehmer>
</TeilnehmerS>
Dieses Dokument besteht, wie jedes XML-Dokument überhaupt, aus
Inhalten, die mit Markup-Symbolen durchsetzt sind. Die spitzen Klammern (<>) und die von ihnen umschlossenen Namen heißen tags2. Tags
begrenzen und bezeichnen Teile des Dokuments, die wir Elemente nen1. Siehe http://xml.apache.org.
2. Alternativ zum engl. Begriff verwenden wir das deutsche Wort Markierung.
Kapitel 1 – Einführung
7
nen, und fügen weitere Informationen hinzu, die zur Definition der Struktur beitragen. Wie man sieht, unterscheidet sich der End-Tag vom StartTag durch den Schrägstrich („/“) als erstes Zeichen nach der öffnenden
spitzen Klammer. Der Text zwischen den Tags ist der Inhalt (Content) des
Elements. Zusammen bilden sie der Inhalt des Dokuments, also die
eigentliche Information, etwa den Inhalt eines Briefes oder den Namen
des Absenders. XML-Dokumente setzen sich grundsätzlich aus diesen
beiden Teilen - Strukturangaben und Inhalt - zusammen.
Die Anweisung <?xml version='1.0'?> in der ersten Zeile ist eine
sog. XML-Deklaration (XML Processing Instruction). Sie teilt einem
XML-Prozessor mit, dass dieses Dokument ein XML-Dokument ist.
Zusätzlich wird die verwendete XML-Version 1.0 und der Zeichensatz
ISO-8859-1 für westeuropäische Sprachen angegeben.
Die Tags <TeilnehmerS> und <Teilnehmer> enthalten weitere
Unterelemente. Sie dienen also der Strukturierung des Dokuments. Das
name-Element dagegen enthält einen Text als Inhalt. In XML können Elemente auch gemischt Unterelemente und Texte als Inhalt haben, z. B.
<i><b>XML</b> in der Praxis</i>.
Die Angaben matrNr='...' und version="..." sind sog. Attribute.
Ein Element kann mehrere Attribute enthalten, die weitere Informationen
über das Element angeben. Die Attributwerte sind in Hochkommata (einfache oder doppelte) eingeschlossen. In unserem Beispiel gibt z. B. das
Attribut matrNr die Matrikelnummer eines Teilnehmers an.
1.5.1 Ein einfaches Stylesheet
Um eine Visualisierung dieses Dokuments z. B. in Form einer HTMLTabelle zu erzeugen, entwickeln wir ein XSLT-Stylesheet. Es definiert für
jedes Element, welche Ausgaben in der Zielsprache produziert werden. Es
können aber auch weitere XSLT-Anweisungen angegeben werden.
Programm 1–2 Stylesheet zur Transformation („teilnehmer0.xsl“)
<?xml version='1.0' encoding='ISO-8859-1'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
Einführung in XML
8
<xsl:template match='/'>
<html>
<head>
<title>
<xsl:value-of select='TeilnehmerS/@vorlesung'/>
</title>
</head>
<body>
<h1 align='center'>
Teilnehmerliste:
<xsl:value-of select='TeilnehmerS/@vorlesung'/>
</h1>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match='TeilnehmerS'>
<table border='1' align='center' cellpadding='5'>
<tr>
<th>Vorname</th>
<th>Name</th>
<th>Matrikelnummer</th>
<th>Semester</th>
<th>Fachbereich</th>
</tr>
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match='Teilnehmer'>
<tr>
<td><xsl:value-of select='vorname'/></td>
<td><xsl:value-of select='name'/></td>
<td><xsl:value-of select='@matrNr'/></td>
<td><xsl:value-of select='semester'/></td>
<td><xsl:value-of select='fachb'/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Da XSLT-Dokumente XML-Dokumente sind, beginnt das Dokument mit
der Anweisung <?xml version="1.0">. Das Element <xsl:styles-
Kapitel 1 – Einführung
9
ist das oberste Element1 in jedem XSLT-Stylesheet. Das Element <xsl:template> verarbeitet ein Element (oder eine Gruppe von
Elementen) aus dem XML-Dokument und produziert eine Ausgabe in der
jeweiligen Zielsprache (hier: HTML). Im Beispiel verarbeitet das erste
<xsl:template> das Wurzelelement des Dokuments ('/'). Es erzeugt
daraus die Ausgabe <html>...</html> und ruft zwischen den HTMLAnweisungen <xsl:apply-templates> auf, um die Unterelemente der
Wurzel an dieser Stelle zu verarbeiten.
heet ...>
Durch den Aufruf von Xalan kann das XML-Dokument mithilfe des
obigen Stylesheets in HTML transformiert werden (siehe Abbildung 1.1).
xalan -in teilnehmer0.xml -xsl teilnehmer0.xsl \
-out teilnehmer0.html
Programm 1–3 Teilnehmerliste in HTML („teilnehmer0.html“)
<html>
<head>
<META http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>Einführung in XML</title>
</head>
<body>
<h1 align="center">
Teilnehmerliste: Einführung in XML</h1>
<table cellpadding="5" align="center" border="1">
<tr>
<th>Vorname</th>
<th>Name</th>
<th>Matrikelnummer</th>
<th>Semester</th>
<th>Fachbereich</th>
</tr>
<tr>
<td>Alex</td><td>Möller</td>
<td>4711</td><td>6</td><td>16</td>
</tr>
1. Wir verwenden ab sofort die Begriffe Element und Tag synonym, d.h. statt genauer
vom „Element mit Start-Tag <xyz>“ zu sprechen, sagen wir „das Element
<xyz>“.
10
Einführung in XML
<tr>
<td>Hans</td><td>Schmidt</td>
<td>4722</td><td>8</td><td>17</td>
</tr>
</table>
</body>
</html>
Dieses HTML-Dokument kann nun in einem beliebigen Browser betrachtet werden.
Der Internet Explorer bietet ab Version 5 umfassende XML-Fähigkeiten. Der eingebaute XML-Parser des Internet Explorers kann XMLDokumente validieren und in einer übersichtlichen, baumartigen Struktur
darstellen. Zusätzlich können XML-Dokumente mithilfe eines XSLT-Stylesheets in HTML transformiert und das Ergebnis im Browser dargestellt
werden. Dazu ist in das XML-Dokument ein Verweis auf das verwendende Stylesheet einzubauen:
<?xml version='1.0' encoding='ISO-8859-1'?>
<?xml-stylesheet type="text/xsl"
href="teilnehmer0.xsl"?>
<TeilnehmerS>
...
</TeilnehmerS>
Der Browser Mozilla kann mittlerweile ebenfalls XML-Dokumente mithilfe von CSS-Stylesheets anzeigen und XSL-Transformationen durchführen.
Kapitel 1 – Einführung
11
Abb. 1–1 Darstellung als HTML-Tabelle
1.5.2 Eine einfache DTD
Das obige XML-Dokument konnte vom Stylesheet-Prozessor bearbeitet
und mithilfe eines Stylesheets in HTML übertragen werden. Es erfüllt
also bestimmte Grundvoraussetzungen eines XML-Dokuments.1 Doch
welche Elemente kann man in einem solchen Dokument verwenden und
welche Attribute haben sie? Anhand welcher Grammatik ist das Dokument aufgebaut? Zur Festlegung dieser Grammatik dienen sowohl Document Type Definitions (DTDs) als auch XML Schemata. Wir betrachten
hier eine einfache DTD zur Definition von Dokumenten des Typs „Teilnehmerliste“.
1. Darauf gehen wir im nächsten Kapitel ein.
Einführung in XML
12
Programm 1–4 DTD für Teilnehmerlisten („teilnehmer0.dtd“)
<!ELEMENT
<!ATTLIST
<!ELEMENT
<!ATTLIST
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
TeilnehmerS (Teilnehmer*)>
TeilnehmerS vorlesung CDATA #REQUIRED>
Teilnehmer (name, vorname, semester, fachb)>
Teilnehmer matrNr NMTOKEN #REQUIRED>
name (#PCDATA)>
vorname (#PCDATA)>
semester (#PCDATA)>
fachb (#PCDATA)>
Zur Definition eines Elements dient die Deklaration
<!ELEMENT name definition>
Die Deklaration eines Attributes geschieht mit
<!ATTLIST elementname attributname definition>
Elemente mit Textinhalt werden mit dem Schlüsselwort (#PCDATA) angegeben (z. B. name). Elemente, die weitere Unterelemente enthalten, werden durch die Auflistung der erlaubten Unterelemente, getrennt durch
Kommata, definiert (z. B. Teilnehmer).
Im XML-Dokument kann dann ein Verweis auf die verwendete DTD
angegeben werden. Das geschieht wie folgt:
<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE TeilnehmerS SYSTEM "teilnehmer0.dtd">
<TeilnehmerS>
...
</TeilnehmerS>
Bei kurzen DTDs bietet es sich an, diese direkt in das XML-Dokument
wie folgt einzubauen:
<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE TeilnehmerS [
<!ELEMENT TeilnehmerS (Teilnehmer*)>
...
]>
<TeilnehmerS>
...
</TeilnehmerS>
Kapitel 1 – Einführung
13
Zur Überprüfung, ob das Dokument den Angaben in der DTD genügt, in
der XML-Terminologie also ein gültiges (valides)1 XML-Dokument ist,
verwenden wir den Xerces-XML-Parser. Er bietet für diesen Zweck unter
anderem das Programm Counter.
kai@labserv:~$ alias xmlParse='java -cp \
/usr/share/doc/libxerces2-java-doc/examples dom.Counter \
-V -n -f'
kai@labserv:~$ xmlParse -v teilnehmer0.xml
teilnehmer0.xml: 250;35;0 ms (11 elems, 3 attrs,
33 spaces, 27 chars)
Um eine Fehlerausgabe zu produzieren, ändern wir in der drittletzten
Zeile von Programm 1–1 das abschließende Tag </fachb> in </fachB>
und rufen xmlParse erneut auf:
kai@labserv:~$ xmlParse -v teilnehmer1.xml
[Fatal Error] teilnehmer1.xml:14:14: The element type
"fachb" must be terminated by the matching end-tag
"</fachb>".
1.6
Zusammenfassung
Wir haben in diesem Kapitel einen ersten Eindruck von XML und den
verwandten Technologien bekommen und dabei einige neue Begriffe und
Definitionen kennengelernt. Dazu wurde ein erstes einfaches XMLDokument entwickelt und mittels eines Stylesheets in HTML transformiert, womit die Entwicklung von XML-Dokumenten, XSLT-Stylesheets
und DTDs veranschaulicht wurde.
In den nächsten Kapiteln gehen wir Schritt für Schritt auf diese Sprachen im Detail ein.
1. Im Gegensatz zu wohlgeformt (well-formed), bei dem nur auf passende Start- und
End-Tags geachtet wird.
15
2 Grundlagen
2.1
Aufbau von XML-Dokumenten
XML-Dokumente haben den folgenden Grundaufbau:
<?xml version='1.0' ...?>
<!DOCTYPE ...>
<rootElement attr="Wert" ...>
<firstElement attr="Wert">
Inhalt von firstElement
</firstElement>
<lastElement attr="Wert">
Inhalt von lastElement
</lastElement>
</rootElement>
Wir gehen im folgenden auf die einzelnen Teile dieses Dokuments ein.
2.1.1 Die XML-Deklaration
Die erste Zeile ist die sog. XML-Deklaration. Sie sollte in jedem XMLDokument enthalten sein.1 Sie kann die folgenden Attribute haben:
■ version
Spezifiziert die verwendete XML-Version im Dokument.
■ encoding
Definiert die im Dokument verwendete Zeichenkodierung. Einige ver1. Ab Version 1.1 des XML-Standards muss die XML-Deklaration angegeben werden.
Einführung in XML
16
breitete Zeichenkodierungen sind: US-ASCII, ISO-8859-1, UTF-8
und UTF-16. Falls encoding nicht angegeben wird, wird die StandardZeichenkodierung UTF-8 verwendet1.
■ standalone
Sagt dem XML-Prozessor, ob irgendwelche andere Dateien geladen
werden müssen.
Die Attribute „encoding“ und „standalone“ sind optional, „version“ muss
angegeben werden.
Anmerkung: Die „Standalone Document Declaration“ ist als Hinweis für
Applikationen gedacht, die ein Dokument vom XML-Prozessor erhalten
und es nicht notwendigerweise vollständig parsen wollen. Der Hinweis
besagt, ob externe „Markup Declarations“ existieren, die einzubinden
wären, damit das Dokument „standalone“ vollständig zu interpretieren ist.
Speziell betrifft das Default-Attribute und Entities (siehe unten), die im
XML-Dokument benötigt werden und in einer externen DTD definiert
werden. Ein validierender Parser prüft diese Notwendigkeit, wenn eine
DTD angegeben ist, unabhängig davon ob standalone den Wert "yes"
oder "no" hat! Der Standard weist darauf hin, dass man algorithmisch ein
Dokument mit notwendigen externen Markup Declarations (standalone="no") in ein solches mit bereits hereingeladenen Erweiterungen
und der dann möglichen Angabe standalone="yes" wandeln kann. Falls
externe Markup Declarations vorhanden sind, aber keine Standalone
Document Declaration angegeben ist, wird standalone="no" angenommen.
2.1.2 Die Dokumenttyp-Deklaration
Falls eine DTD (Document Type Definition) verwendet werden soll, so
muss diese deklariert werden. Die Instruktion kann gegenwärtig die zwei
Formen
1. Zeichenkodierungen sind Teilbereiche vom Unicode-Zeichensatz, die mit 8 Bit dargestellt werden. Genauere Informationen kann man dem Dokument unter:
http://www.w3.org/Markup/html-spec/charset-harmful.html
entnehmen.
Kapitel 2 – Grundlagen
17
<!DOCTYPE root-element PUBLIC "name" "URL-of-DTD">
<!DOCTYPE root-element SYSTEM "URL-of-DTD">
annehmen.
Mit PUBLIC werden (in Zukunft!) öffentlich zugängliche DTDs spezifiziert. Diese sollen als registrierter Name in einem internen oder externen
Repositorium bekannt sein. Schlägt die Ermittlung über name fehl, wird
auf die folgende URL zugegriffen. Alternativ kann man sofort auf privat
verfügbare DTDs zugreifen, dies geht mit der Variante SYSTEM.
Die Angabe name bei PUBLIC benutzt den sog. Formal Public Identifier (FPI)1. Diese Namenskonvention benutzt einen Namen mit vier Segmenten, die durch Doppelschrägstriche (slashes) getrennt sind, z. B.:
"-//O`Reilly//DTD//EN"
Das Minus deutet auf einen unregistrierten Namen hin, der ggf. nicht eindeutig ist. Ein Plus (+) wäre ein registrierter Eintrag. Das zweite Segment
ist der Autor oder die Organisation, die den Namen veröffentlicht, das
dritte Segment die Inhaltsform (hier DTD). Zuletzt folgt der Sprachcode,
hier EN für Englisch.
2.1.3 Elemente
Elemente sind die Grundbausteine von XML-Dokumenten. In XML werden Elemente durch Tags in spitzen Klammern in der Form
<elementname> ... </elementname> eingeklammert.
Der Elementname kann eine beliebige Anzahl von Buchstaben, Zahlen, Bindestrichen, Punkten und Unterstrichen enthalten, darf aber nicht
mit Punkt, Bindestrich oder einer Ziffer beginnen.2
Das Doppelpunktzeichen (:) wird als Trenner für Namensräume verwendet (später in diesem Kapitel). Leerzeichen, Tabulatoren, Zeilenum1. ISO-8879
2. Sie müssen aber dabei beachten, dass jeder XML-Prozessor bzgl. der Länge des
Namens Einschränkungen macht. Es existiert zwar keine Vorschrift, doch in der
Praxis werden Elementnamen mit bis zu 40 Zeichen akzeptiert.
18
Einführung in XML
brüche, Gleichheitzeichen und Anführungszeichen sind Separatoren und
dürfen deshalb in Element- und Attributnamen nicht verwendet werden.
Sonderzeichen wie & oder ? dürfen ebenfalls nicht in Elementnamen enthalten sein.1
Zwischen der ersten spitzen Klammer und dem Elementnamen darf
kein Leerzeichen stehen (z. B. < Buch>), dagegen können beliebige
Trennzeichen nach dem Elementnamen, und zwischen den Attributen stehen. Damit kann man ein Element auf mehreren Zeilen verteilen:
<Buch
ISBN="X-3-89721-286-2" titel='Einführung in XML'>
...
</Buch>
Da XML Unicode als Zeichensatz verwendet, können Elementnamen in
einer beliebigen, von Unicode unterstützten Sprache geschrieben werden.
Beispiele für gültige Elementnamen in den Start-Tags sind
<_>, <_a>, <b4711>, <VON-BIS>, <Name.Vorname>.
Beispiele für nicht gültige Elementnamen in Tags sind etwa
<4711>, <.punkt>, <a&b>, <verstanden?>.
Start- und Endtags
Ein Element besteht aus einem Start-Tag, einem Inhalt und einem EndTag. Der Inhalt kann aus reinem Text bestehen, aus weiteren Unterelementen oder einer Mischung aus beidem. Die Ende-Markierung (EndTag) wird durch </elementname> angegeben, und darf nicht, wie in
HTML, weggelassen werden. Ist der Inhalt leer (leeres Element), so kann
man Start- und End-Tag zu einem Tag zusammenziehen indem man ein '/'Zeichen an das Ende des Tags vor die schließende spitze Klammer
schreibt:
<leeresElement attr1='...' attrn='...'/>
1. Die genaue Beschreibung in EBNF-Form kann man in der Standardspezifikation
nachlesen.
Kapitel 2 – Grundlagen
19
Alternativ kann man ein leeres Elemente aber auch mit abschließendem
eigenen End-Tag wie üblich angeben. Daher sind <br></br> und <br/>
äquivalente Schreibweisen.
Der Start- und End-Tag eines Elements müssen sich beide im gleichen
Eltern-Element befinden. So ist z. B.
<a>So <b>geht es</a> nicht</b>
nicht erlaubt.
Da das Kleinerzeichen (<) für den Beginn eines Tags steht, darf es
nicht direkt im Text verwendet werden. Dazu können die vordefinierten
Entities für < (<) und für > (>) verwendet werden.
Sehr wichtig bei der Texteingabe ist darauf zu achten, dass XML alle
Leerzeichen, Zeilenumbrüche und Tabulatorzeichen beibehält. Dies ist
bei Programmiersprachen und anderen Auszeichnungssprachen, wo Leerzeichen in der Regel ignoriert werden (siehe xml:space weiter unten),
anders.
2.1.4 Attribute
Mit Attributen kann man Elementen zusätzliche oder genauere Informationen hinzufügen. Man kann z. B. Elementen eindeutige Bezeichner
durch ein Attribut geben, oder eine Eigenschaft über dieses Element
beschreiben (Beispielsweise das href-Attribut für das A-Element in
HTML).
Eine Attributangabe besteht aus einem Attributnamen, einem Gleichheitszeichen und einem Wert in Anführungszeichen. Dabei können einfache oder doppelte Anführungszeichen verwendet werden:
attrname="Wert von attrname" oder
attrname='Wert von attrname'.
Für Attributnamen gelten die gleichen Regeln wie für Elementnamen. Ein
Element darf ein Attribut nur einmal enthalten.
<x a="wert 1" a="wert 2"/> ist nicht erlaubt.
Ein Element kann eine beliebige Anzahl von Attributen haben, solange
jedes über einen eindeutigen Namen verfügt.
Einführung in XML
20
Reservierte Attribute
Die folgenden Attributnamen sind im XML-Standard mit einer festen
Bedeutung reserviert:
■ xml:lang
Mit diesem Attribut wird die Sprache des Elements angegeben. Dies
ist nützlich für die Erstellung von mehrsprachigen Dokumenten. Die
Angabe verwendet einen zweibuchstabigen Sprachcode (Kleinschreibung wie de, en ist üblich). Ein weiterer Qualifier kann verwendet
werden, um eine Sprach-Variante wie en-US zu spezifizieren. Vereinbarungsgemäß besteht dieser Qualifier aus zwei Großbuchstaben.
Zur Definition von eigenen Sprachen kann der Sprach-Code x verwendet werden. Beispiele könnten etwa sein: x-pascal, x-java usw.
■ xml:space
xml:space="default | preserve"
■
Bestimmt, ob die Whitespaces (Leerzeichen, Neue-Zeile, Tabulator)
wie angegeben erhalten bleiben sollen (preserve) oder ob beliebige
Ersetzungen erlaubt sind.
xml:link und xml:attribute
Diese beiden Attribute wurden ursprünglich im Zusammenhang mit
Link-Elementen eingesetzt. Seit der Einführung des XLink-Namensraums sind sie veraltet und werden nicht mehr verwendet.
2.1.5 Namensräume
In XML-Dokumenten können Elemente aus verschiedenen Sprachen verwendet werden. Sie können z. B. eine mathematische Formel als
MathML-Element in einem HTML-Dokument einbauen. Damit der
XML-Prozessor (HTML-Browser) zwischen den Elementen beider Sprachen unterscheiden kann und diese unterschiedlich behandelt (z. B. das
Plugin für die Darstellung von MathML aufruft), wurden in XML
Namensräume eingeführt.
Bevor ein Namensraum verwendet werden kann, muss dieser zuerst
im Dokument deklariert werden. Die Deklaration erfolgt in der Form
eines Attributes innerhalb eines Elements:
Kapitel 2 – Grundlagen
21
<namensraum:elementName xmlns:namensraum="url">
Man kann in einem Dokument mehrere Namensräume verwenden. Der
deklarierte Namensraum kann in dem Element selbst und allen Nachkommen genutzt werden. Zur Verwendung von Namensräumen werden sie
durch ein Namensraum-Präfix gefolgt von einem Doppelpunkt gefolgt
vom lokalen Namen des Elements angegeben.
Der Wert des xmlns:-Attributs ist eine URL, die gewöhnlich zu der
Organisation gehört, die den Namensraum unterhält. Es ist jedoch nicht
erforderlich, dass der XML-Prozessor diese URL benutzt. Die Angabe der
URL dient lediglich der eindeutigen Bezeichnung des Namensraums.
Durch die Wahl einer URL für diesen Zweck könnte jedoch eine eventuelle weitere Verwendung dieser Angabe in der Zukunft möglich sein.1
Im folgenden Beispiel werden die Namensräume vorlesung und
skript deklariert:
<?xml version="1.0" standalone="yes"?>
<vorlesung:beispiel xmlns:vorlesung=
"http://www.informatik.uni-kassel.de/~wegner/">
<vorlesung:titel>Namensraum</vorlesung:titel>
<vorlesung:code>4711-9999-XYZ</vorlesung:code>
<vorlesung:anfang>
Auch Zwerge haben mal klein angefangen
</vorlesung:anfang>
<skript:inhaltsverz xmlns:skript=
'http://www.informatik.uni-kassel.de/skript/'>
<skript:kap nr='1'>
<skript:titel>Einführung</skript:titel>
</skript:kap>
</skript:inhaltsverz>
</vorlesung:beispiel>
Wir können einen Namensraum als Standard-Namensraum deklarieren,
indem wir den Doppelpunkt (:) und den Namen im xmlns-Attribut weglassen. In unserem Beispiel können wir z. B. vorlesung als StandardNamensraum benutzen:
1. Vorstellbar wäre das Validieren des Elements anhand einer DTD oder eines XML
Schemas, die sich unter der angegeben URL befinden.
22
Einführung in XML
<?xml version="1.0" standalone="yes"?>
<beispiel xmlns=
"http://www.informatik.uni-kassel.de/~wegner/">
<titel>Namensraum</titel>
<code>4711-9999-XYZ</code>
<anfang>
Auch Zwerge haben mal klein angefangen
</anfang>
<skript:inhaltsverz xmlns:skript=
'http://www.informatik.uni-kassel.de/skript/'>
<skript:kap nr='1'>
<skript:titel>Einführung</skript:titel>
</skript:kap>
</skript:inhaltsverz>
</beispiel>
Die Verwendung von Namensräumen in Verbindung mit DTDs ist problematisch, und in dem jetzigen Standard noch nicht sauber gelöst. Möchten
Sie ein Dokument mit Namensräumen anhand einer DTD validieren, so
werden die Elemente aus den „fremden“ Namensräumen nicht etwa ignoriert, sondern müssen in der DTD definiert sein. Aus diesen und anderen
Gründen sind Namensräume unter XML-Planern umstritten. Es ist aber
klar, dass beide Konzepte, Strukturerzwingung und Namensräume, prinzipiell erforderlich sind und unter einen Hut gebracht werden müssen.
2.1.6 Wohlgeformte XML-Dokumente
Im Laufe der Entwicklung von HTML haben sich gewisse tolerierte Vereinfachungen und Unsauberkeiten eingeschlichen. So darf man bei Tabellen das schließende Element eines Tabellenfeldes </TD> weglassen, allerdings auf die Gefahr hin, dass das letzte Feld nicht richtig zugeordnet
wird.
Dies ist in XML nicht zulässig. Alle Elemente müssen paarweise auftreten und "wohlgeschachtelt" (properly nested) sein, außer natürlich bei
leeren Elementen wie etwa <picture src='...' />. Damit ist
<Abschnitt>
<Para>
Blablabla
<Para>
Kapitel 2 – Grundlagen
23
noch mehr bla
</Abschnitt>
ungültiges XML (es fehlen jeweils die </Para> Endmarken).
Genauso wird
<b><i>Achtung: gekauft wie gesehen</b></i>
zwar in HTML akzeptiert, in XML aber zurückgewiesen.
Andersherum ist ein leeres XML-Element in HTML eigentlich ein
Fehler, z. B. die waagerechte Linie <HR> (horizontal ruler). In „XMLForm“ muss stattdessen <HR/> geschrieben werden.
Argumente von Attributen müssen in XML immer in Hochkommata
eingeschlossen werden. HTML Browser erlauben auch das Weglassen,
sofern der Wert keine Leerstellen enthält. Es sind sowohl einzelne als
auch doppelte Hochkommata erlaubt.
Zuletzt sei darauf hingewiesen, dass HTML bekanntlich nicht auf
Groß- und Kleinschreibung achtet, XML dagegen case sensitive ist!
<h2>Überschrift</H2> wird deshalb als XML-Dokument zurückgewiesen, ginge in HTML aber durch.
Weiterhin ist zu beachten:
■
■
Ein XML-Dokument besitzt ein eindeutiges Wurzelelement.
Die Markierungszeichen < und & dürfen nicht als Textzeichen auftreten, sondern müssen durch sog. Entity-Referenzen ersetzt werden.
Genauso muss ]]> durch ]]> ersetzt werden (> darf direkt angegeben werden).
Dokumente, die korrekt geschachtelt sind und die obigen Punkte beachten, sind wohlgeformt (well-formed). Genügt ein Dokument auch einer
zugehörigen DTD, dann nennt der Standard es gültig (valid).
2.1.7 Prozessor-Instruktionen
Prozessor-Instruktionen (PI, Processing Instructions) sind dazu gedacht,
Anweisungen an externe Anwendungen in einem XML-Dokument einzu-
24
Einführung in XML
bauen. Diese werden nur von der adressierten Anwendung verstanden und
interpretiert. Sie werden von anderen Anwendungen ignoriert.
Eine PI sieht wie folgt aus
<?anwendung attr1='Wert 1' ...?>
Ein Beispiel für eine Prozessor-Instruktion ist der Hinweis auf die Verwendung eines Stylesheets:
<?xml-stylesheet type="text/xsl" href="filename.xsl"?>
Dies teilt einem Stylesheet-Prozessor mit, das XML-Dokument anhand
des Stylesheets filename.xsl zu transformieren.
2.1.8 Entities
Ähnlich wie bei Makros lassen sich Zeichenfolgen mit einem Bezeichner
versehen. Einige Standardvorgaben (z. B. &) werden von XML vordefiniert und müssen nicht gesondert angegeben werden. Andere können
in einer DTD definiert werden.
Eine Entity ename wird in einem Dokument durch &ename; aufgerufen.
2.1.9 Kommentare
Kommentare werden in XML durch <!-- ... --> angegeben. In einem
Kommentar können beliebige Texte vorkommen. Innerhalb eines Kommentars dürfen aber keine doppelten Bindestriche (hyphens) enthalten
sein.
Kommentare dürfen überall im Dokument stehen, nur nicht vor der
XML-Deklaration und innerhalb von Tags. Sie werden von einem XMLProzessor ignoriert.
2.1.10 CDATA-Abschnitte
Mit dieser sog. Character-Data Sektion (nicht zu verwechseln mit der
PCDATA-Angabe in DTDs) lassen sich Zeichenfolgen angeben, die der
Kapitel 2 – Grundlagen
25
Parser nicht interpretiert. Die Folge muss mit <![CDATA[ anfangen und
mit ]]> aufhören. Als Beispiel könnte man schreiben
<![CDATA[In diesem XML-Kapitel besprechen wir
"Deklarationen", z. B. <?xml ...?> und <!DOCTYPE ...>,
CDATA[...]-Sektionen & vieles mehr
]]>
Andererseits sollte man innerhalb einer CDATA-Sektion keine EntityReferenzen wie &, <, >, " und ' verwenden, da
diese nicht aufgelöst werden.
27
3 Document Type Definitions
3.1
Elementdeklarationen
Entschließt man sich, ein Dokumentenschema mittels der älteren, weit
verbreiteten Document Type Definition (DTD) anzugeben, dann gilt:
Jedes in einem gültigen XML-Dokument auftretende Element muss in der
DTD deklariert sein. Dies geschieht mit der Elementdeklaration
<!ELEMENT elementname rule>
wobei der Elementname ohne spitze Klammern angegeben wird. Was hier
als rule bezeichnet wird, legt fest, was zwischen der Start- und Endmarkierung des Elements stehen darf.
3.1.1 ANY und #PCDATA
Im einfachsten Fall erlaubt man mit einer Angabe
<!ELEMENT book ANY>
beliebige Daten, auch andere Tags.
Will man andererseits nur „Nutzdaten“ ohne Metazeichen (sog. parsed
character data) erlauben, dann lautet die Angabe #PCDATA.
<!ELEMENT title (#PCDATA)>
Damit wäre in einem XML-Dokument
<title></title>
<title>Skriptum XML-Vorlesung © L. Wegner</title>
28
Einführung in XML
legal, nicht jedoch
<title><emphasis>Skriptum XML-Vorlesung</emphasis></title>
Soll ein Element in einem anderen Element auftreten, gibt man das mit
runden Klammern an. Muss in unserem Beispiel ein Buchtitel in einem
Buch erscheinen, lauten die Deklarationen:
<!ELEMENT book (title)>
<!ELEMENT title (#PCDATA)>
3.1.2 Mehrfache Elemente
Sollen mehrere Elemente in einer vorgegebenen Reihenfolge auftreten,
gibt man diese durch Komma getrennt an:
<!ELEMENT book (title, authors)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT authors (#PCDATA)>
Damit kann man in einem XML-Dokument schreiben:
<book>
<title>Skriptum XML-Vorlesung</title>
<authors>Wegner</authors>
</book>
Ein Vertauschen oder Weglassen eines Elements wäre aber nicht möglich.
Dies erreicht man mit der Alternative, allerdings muss genau eine der
Alternativen erscheinen!
<!ELEMENT book (title | authors)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT authors (#PCDATA)>
3.1.3 Gruppierung und Wiederholung
Wie man dies aus den kontextfreien Grammatiken in EBNF-Notation und
von der Shell-Programmierung kennt, kann man Gruppierungen und
(optionale) Wiederholungen angeben. Soll ein Buch entweder einen Titel
gefolgt von Autoren, oder nur eine Beschreibung enthalten, schreibt man:
<!ELEMENT book ((title, authors) | description)>
Kapitel 3 – Document Type Definitions
29
<!ELEMENT title (#PCDATA)>
<!ELEMENT authors (#PCDATA)>
<!ELEMENT description (#PCDATA)>
Das optionale Auftreten gibt man mit ?, das mindestens einmalige mit +,
und das null- oder mehrmalige Auftreten mit * an. Die Metazeichen stehen unmittelbar nach den Elementnamen oder den Gruppen, auf die sie
sich beziehen sollen. Das Beispiel in [21] lautet:
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
reviews (rating, synopsis?, comments+)*>
rating ((tutorial | reference)*, overall)>
synopsis (#PCDATA)>
comments (#PCDATA)>
tutorial (#PCDATA)>
reference (#PCDATA)>
overall (#PCDATA)>
3.1.4 Leere Elemente
Auch das Auftreten leerer Elemente in einem XML-Dokument muss
angegeben werden.
<!ELEMENT elementname EMPTY>
Damit kann durch <!ELEMENT statuscode EMPTY> im XML-Dokument
entweder
<statuscode></statuscode>
oder
<statuscode/>
stehen.
3.2
Entities und Notationen
Ähnlich wie bei Makros lassen sich Zeichenfolgen mit einem Bezeichner
versehen. Einige Standardvorgaben (z. B. &) haben wir schon gesehen, diese müssen nicht gesondert angegeben werden. Andere gibt man
wie folgt an:
<!ENTITY copyright "©">
30
Einführung in XML
Im Dokument schreibt man dann
<title>XML-Skript &copyright; L. Wegner 2000</title>
und erzeugt damit die Ausgabe XML-Skript © L. Wegner 2000.
Wie bei Makros üblich, dürfen die Angaben nicht zirkulär sein. Auch
dürfen in den Substitutionszeichen keine weiteren Metazeichen auftauchen, da die Ersetzung erst im XML-Dokument erfolgt, nicht bereits in
der DTD. Dies ist bei den folgenden parametrischen Entities anders.
3.2.1 Parameter-Entities
Diese werden nur in DTDs verwandt und können als deklarierte Elemente
nicht in XML-Dokumenten auftreten. Sie stellen abkürzende Schreibweisen für DTDs dar. Vor dem Namen erscheint ein Prozentzeichen.
<!ENTITY % name "replacement characters">
Das Beispiel in [21] lautet:
<!ENTITY % pcdata "(#PCDATA)">
<!ELEMENT authortitle %pcdata;>
Wie zuvor dürfen die Deklarationen nicht zirkulär sein und ein ParameterEntity muss zuerst deklariert sein, bevor es verwendet werden kann.
3.2.2 Externe Parameter-Entities
Externe Parameter-Entities dienen dazu, große DTDs auf verschiedene
Dateien aufteilen zu können. Mit der Parameter-Entity-Angabe kann man
Teile einer DTD in eine andere importieren.
Die Deklaration ähnelt der Deklaration einer Parameter-Entity, doch
statt einen Ersatztextes gibt es hier eine PUBLIC- oder SYSTEM-Angabe.
Beispiel:
<!ENTITY % format-elemente SYSTEM "formats.mod">
Durch den Aufruf von %format-elemente; in einer DTD wird der Inhalt
der Datei formats.mod an der Stelle des Aufrufs eingebaut.
Kapitel 3 – Document Type Definitions
31
3.2.3 Externe Entities
Hier sollen Daten, die von einer anzugebenden URI stammen, dynamisch
in das XML-Dokument geladen werden.
<!ENTITY fremd SYSTEM "fremd.xml">
Befinden sich in fremd.xml (ggf. an einem anderen Ort) XML-Daten,
zum Beispiel
<einbau>Wer's glaubt wird seelig</einbau>
wird dieser Text zur Laufzeit in das XML-Dokument aufgenommen, das
die Referenz &fremd; enthält.1
3.2.4 Notationen
Der Zweck einer „Notations-Vereinbarung“ (notation declaration) ist es,
ein Format für eine externe Datei anzugeben, die kein XML-Dokument
ist. Dies könnte ein Bild oder eine Audiodatei sein. Mit der Formatangabe
kann man jetzt auf diese Datei verweisen.
Die zwei allgemeinen Formen einer Notationsvereinbarung sind:
<!NOTATION nname PUBLIC "std">
<!NOTATION nname SYSTEM "url">
wobei nname der selbstvergebene Name für die Notation ist, std ist der
veröffentlichte Name einer „public notation“, und url ist ein Hinweis auf
ein Programm, das die Datei, deren Notation hier angegeben wird, darstellen kann.
In vier Schritten verbindet man ein Attribut mit einer Notation:
1. Die Notation vereinbaren. Beispiel:
<!NOTATION jpeg PUBLIC "JPG 1.0">
1. In fremd.xml darf &fremd; natürlich nicht verwendet werden.
Einführung in XML
32
2. Das Entity vereinbaren (NDATA steht für notation data). Z. B.:
<!ENTITY bogie-pic SYSTEM
"http://stars.com/bogart.jpg" NDATA jpeg>
3. Das Attribut vereinbaren als vom Typ ENTITY. Zum Beispiel:
<!ATTLIST star-bio pin-shot ENTITY #REQUIRED>
4. Das Attribut verwenden:
<star-bio pin-shot="bogie-pic">...</star-bio>
Anmerkung: Beispiel aus
http://infohost.nmt.edu/tcc/help/pubs/dtd/notationsect.html
Andere Beispiele für den in
<!NOTATION Name Identifier>
stehenden Identifier folgen. Es ist jedoch nicht genau festgelegt, wie
Identifier anzugeben ist.
Beispiel
Bedeutung
SYSTEM "application/x-troff"
MIME-Type
SYSTEM "ISO 8601:1988"
Internationale Standards
SYSTEM "http://www.w3.org/TR/NOTE-datetime"
Die URL über ein technisches
Dokument im Internet
SYSTEM "/usr/local/bin/xv"
Ein Softwareprogramm auf dem
lokalen Rechner
PUBLIC "-//FPI"
Eine URI
XML macht keine genauen Angaben über den Umgang mit nicht analysierten Daten. Die Interpretation bleibt also der Anwendung überlassen,
die das Dokument bearbeitet (XML-Prozessor). Es ist daher ratsam, Notationen zu vermeiden und stattdessen XML-Instruktionen zu verwenden.
Kapitel 3 – Document Type Definitions
33
Diese werden bekanntlich von anderen Anwendungen ignoriert, falls sie
nicht „verstanden“ werden.
3.2.5 Unparsed Entities
Wie oben gezeigt lassen sich Daten, die kein XML darstellen, in XMLDokumente einbauen. Üblich sind z. B. Bilder. Beispiel:
<!ENTITY meinkopf SYSTEM "test.jpg" NDATA jpg>
Speziell wird damit durch das Schlüsselword NDATA (notation data) angegeben, um welche Art von Rohdaten es sich handelt, hier ein jpg-Pixelbild. Dabei soll jpg als Notation dem XML-Prozessor bekannt sein. Beispiel:
<?xml version='1.0'?>
<!DOCTYPE rootElement [
<!ELEMENT rootElement (img)>
<!ELEMENT img EMPTY>
<!ATTLIST img src ENTITY #REQUIRED>
<!NOTATION jpg SYSTEM "image/jpeg">
<!ENTITY meinkopf SYSTEM "test.jpg" NDATA jpg>
]>
<rootElement>
<img src='meinkopf'/>
</rootElement>
Man achte darauf, dass hier meinkopf statt &meinkopf; steht, da das
Attribut src als ENTITY definiert wurde. Die Aufgabe, ungeparste Entities
aufzulösen, übernimmt der XML-Prozessor, der das Dokument verarbeitet. In XSLT z. B. steht dafür die Funktion unparsed-entity-uri zur
Verfügung. Damit lassen sich Entities anhand ihrer Spezifikation in der
DTD auflösen. Somit wird das <img>-Element im Stylesheet wie folgt
verarbeitet:
Einführung in XML
34
<xslt:template match='img'>
<xslt:element name='img'>
<xslt:attribute name='src'>
<xslt:value-of select='unparsed-entity-uri(@src)'/>
</xslt:attribute>
</xslt:element>
</xslt:template>
Wobei @src in XSLT den Wert des Attributes src ausgibt. Xalan produziert die folgende Ausgabe in HTML:
<img src="file:///home/staff/morad/ ... /XML/test.jpg"/>
3.2.6 Zusammenfassung Entities
In [7, p. 52] werden Entities mit der folgenden Abbildung klassifiziert.
ENTITIES
➀
➁
Parameter-E.
interne
externe
➂
➃
allgemeine
➆
Zeichen-E.
vordefinierte
➅
vom Parser
nicht ersetzte
Namendefinierte
Zahlendefinierte
➄
➇
mit gemischtem
Inhalt
interne
externe
➈
Kapitel 3 – Document Type Definitions
35
In [28, p. 57] werden Entities wie folgt zusammengefasst (die Nummern
in Kreisen stellen den Bezug zu der Klassifikation oben her):
• Standard entities ➃, etwa <
werden intern deklariert, keine Vereinbarung nötig.
• Character entities ➄, etwa ‪ als Zeichenersatz für Zeichen außerhalb des 7-bit ASCII-Zeichensatzes oder für ein Zeichen, das nicht auf der Tastatur erscheint. Zusatz: Werden dafür
Namen vergeben, etwa wie für den ISO-8879 Zeichensatz, dann
trifft dies den Fall ➅
• Internal Entities ➇ für längere Texte, die selber wieder Markup
enthalten können und die den Ersatztext in der Entity-Deklaration
selbst stehen haben. Geeignet für häufige Ersetzungen in einem
Dokument.
• External Entities ➈ für längere Texte inkl. Markup, wenn die
Ersetzungen konsistent über mehrere Dokumente erfolgen sollen;
daher werden sie in einer externen Datei gespeichert (sog. XMLFragmente); das nach dem Import zusammengesetzte XML-Dokument muss insgesamt den Wohlgeformtheitsregeln genügen.
• External unparsed entities ➆ für Inhalte nichttextueller Art, z.B.
Graphiken, die der Parser nicht verarbeiten kann.
• Parameter entities ➀ für Ersetzungen innerhalb einer DTD, etwa
weil mehrere Element-Deklarationen gemeinsame Teile haben;
gibt es als interne oder externe (aus externer Datei) Variante. Verwendet das %-Zeichen bei Deklaration und Aufruf.
3.3
Attributlisten
Nach der Deklaration eines Elements können dessen Attribute als Attributliste deklariert werden. Dabei kann man für ein Element mehrere Attributlisten spezifizieren. Die Vereinbarung hat die folgende Syntax:
<!ATTLIST Name
Attname1 Attype1 Attbeschr1
Attname2 Attype2 Attbeschr2
...
>
Einführung in XML
36
Die Deklaration beginnt mit der Zeichenkette <!ATTLIST. Als zweites
kommt der Name des Elements, für den die Attribute definiert werden.
Danach folgt eine beliebige Anzahl von Attributdeklarationen.
Eine Attributdeklaration besteht aus einem Attributnamen, dessen
Datentyp oder aufgezählten Werten und der Beschreibung des Attributverhaltens. Im folgenden Beispiel werden z. B. die drei Attribute id vom
Typ ID, name vom Typ CDATA und gruppe als Aufzählungstyp für das Element benutzer definiert:
<!ATTLIST benutzer
id
ID
#REQUIRED
name
CDATA
#IMPLIED
gruppe
(student | mitarb | gast) "gast"
>
Es folgt eine Liste der möglichen Attribut-Typen:
(Zeichendaten)
Attribute mit diesem Typ können beliebige Zeichendaten als Werte
bekommen.1 Beispiel dafür:
■ CDATA
titel="In 80 Tagen um die Welt"
erklaerung="4! = 4*3! = 4*3*2 = 24"
signatur="023 25 Wir OU 5114"
Anführungszeichen müssen durch Zeichen-Entity ersetzt werden oder
man verwendet einzelne Anführungszeichen ('...') in doppelten
("...") bzw. umgekehrt. Beispiel:
veranstaltung='Diskussion über die "Agenda 2010"'
■
Tabulatoren und Newline-Zeichen werden in Leerzeichen umgewandelt. Eine Kette von Leerzeichen bleibt dagegen erhalten.
NMTOKEN (Namens-Token)
Ein Wert bei NMTOKEN ist eine Zeichenkette (eine einzelne), die den
Regeln für Element- und Attributnamen entspricht, allerdings keine
Einschränkungen bzgl. des ersten Zeichens macht. Alle Leerzeichen
werden vom XML-Prozessor entfernt. Beispiele:
1. In allen Attribut-Typen dürfen Elemente oder Prozessorinstruktionen nicht verwendet werden.
Kapitel 3 – Document Type Definitions
37
datei='install.sh'
rechner='141.51.8.4'
(Namens-Token-Listen)
Eine Liste von NMTOKEN-Werte, die durch Leerzeichen getrennt werden. Zusätzliche Leerzeichen werden vom XML-Prozessor entfernt.
Beispiel:
■ NMTOKENS
artikel='die der das'
farben='rot gelb blau'
(Eindeutige Bezeichner)
Ein Wert dieses Typs verleiht seinem Element einen Label, der innerhalb des ganzen Dokuments eindeutig ist. Es darf daher kein anderes
Element mit demselben Attributwert im Dokument geben. Der Inhalt
folgt den Formvorschriften für Element- und Attributnamen.
Üblicherweise wird ID mit dem Schlüsselwort #REQUIRED verwendet.
Zwingend ist dies allerdings nicht. Beispiel:
■ ID
urn="urn:nbn:de:hebis:34-593"
buchnr="b17010808"
(Verweis)
Ähnlich wie ID, dient allerdings zum Verweis auf andere Elemente im
Dokument durch die Angabe ihrer ID. Existiert im Dokument kein
Element mit dieser ID, so muss der XML-Prozessor einen Fehler produzieren. Beispiel:
■ IDREF
author-id="a4711"
(Verweisliste)
Die Syntax ist ähnlich wie NMTOKENS. Die aufgelisteten Werte müssen
aber auf im Dokument existierende Element mit diesen ID-Werten verweisen:
■ IDREFS
authors-id="a4711 a5711 a6711"
(Entity-Name)
Dieser Typ akzeptiert den Namen einer nicht-geparsten Entity als
Wert. Beispiel:
■ ENTITY
<!ENTITY meinkopf SYSTEM "test.jpg" NDATA jpg>
<!ATTLIST img src ENTITY #REQUIRED>
Im XML-Dokument wird dieser Attribut wie folgt verwendet:
<img src="meinkopf">
Einführung in XML
38
(Entity-Namenliste)
Der Wert dieses Attributs ist eine Liste von Entity-Namen.
Aufzählungsliste
Hier spezifiziert man eine Liste von Namens-Token, aus denen das
Attribut einen Wert annehmen kann. Die Werte der Liste werden durch
| voneinander getrennt, z. B.:
■ ENTITIES
■
<!ATTLIST veranstaltung
tag (Mo | Di | Mi | Do | Fr | Sa | So) "Sa">
Ein Attribut kann nur einen dieser Werte bekommen. Falls ein Standardwert spezifiziert ist, so muss dieser auch in der Liste auftreten.
(Notationsliste)
Der Wert eines NOTATION-Attributs besteht aus einem Namenstoken
aus einer bei der Deklaration des Typs angegebenen Liste von möglichen Werten. Ein Beispiel aus [7] lautet:
■ NOTATION
<?xml version="1.0"?>
<!DOCTYPE doc [
<!ELEMENT doc (listing*)>
<!ELEMENT listing (#PCDATA)>
<!NOTATION schema-lisp SYSTEM "IEEE 1178-1990">
<!NOTATION ansi-c SYSTEM "ISO/IEC 8899:1999">
<!ATTLIST listing format NOTATION
(schema-lisp | ansi-c) #REQUIRED>
]>
<doc>
<listing format='schema-lisp'>
(defun fact (lambda (n)
(if (= n 1) 1 (fact (- n 1)))))
</listing>
<listing format='ansi-c'>
int fact(int n) {
if (n == 1) return 1;
return n * fact (n - 1);
}
</listing>
</doc>
Dabei beschreibt hier die Notation, wie Textdaten interpretiert werden
Kapitel 3 – Document Type Definitions
39
sollen. Die externen Identifier stammen von den internationalen Standardinstitutionen IEEE und ISO.
Nach der Spezifikation des Typs eines Attributes kann sein Verhalten spezifiziert werden. Dazu existieren die folgenden Möglichkeiten:
■
Standardwert zuweisen
Wenn dieses Attribut im XML-Dokument nicht angegeben wird, wird
der Standardwert vom XML-Prozessor angenommen. Dies macht
Sinn, wenn ein Wert sehr häufig für ein Element vorkommt. Zum Beispiel:
<!ATTLIST article language (en | de | fr) "en">
■
Das Attribut ist optional (#IMPLIED)
Mit IMPLIED wird ausgedruckt, dass das Attribut keinen Standardwert
besitzt und dass das Attribut optional ist. Zum Beispiel:
<!ATTLIST article pages CDATA #IMPLIED>
■
Attribut muss angegeben werden (#REQUIRED)
Das Weglassen des Attributes ist nicht erlaubt. Zum Beispiel:
<!ATTLIST article title CDATA #REQUIRED>
■
Der Wert ist bereits gesetzt, und der Benutzer kann ihn nicht
ändern (#FIXED)
Für den Fall, dass ein Attribut stets denselben Wert hat, kann man
damit einiges an Schreibarbeit sparen. Zum Beispiel:
<!ATTLIST article license CDATA #FIXED "free">
Das Attribut license kann weggelassen werden, gibt man es an, muss
es den Wert free haben.
3.4
Modularisierung
DTDs können ziemlich unübersichtlich werden. Daher wurden Möglichkeiten geschaffen, eine DTD auf mehrere Module aufzuteilen. Damit
kann man
■
■
■
eine DTD leichter warten,
die Module einer DTD auf mehrere Entwickler verteilen,
die DTD durch bedingte Abschnitte konfigurieren und
Einführung in XML
40
■
Fehler in einer DTD besser finden und korrigieren.
XML stellt zwei Möglichkeiten zur Modularisierung einer DTD zur Verfügung:
■
■
Aufspalten der DTD auf mehrere Module. Die Module werden mit
externen Parameter-Entities importiert.
Bedingte Abschnitte.
3.4.1 Importieren von Modulen
Man kann eine DTD auf mehrere Module aufteilen. Diese können dann
durch Parameter-Entities in einer anderen DTD importiert werden. Beispiel:
<!ELEMENT script (titel | autor | inhalt)>
<!ENTITY % script.formats SYSTEM "formats.mod">
<!ENTITY % script.struct SYSTEM "struct.mod">
<!-- Lokale Deklarationen -->
...
<!-- hier importieren -->
%script.formats;
%script.struct;
...
Die Endung .mod ist keine Vorschrift, jedoch die übliche Bezeichnung,
um die Datei als eine Sammlung von Deklarationen und nicht als eigenständige DTD zu markieren.
Da es in DTDs keine Möglichkeit für lokale Definitionen mit Sichtbarkeitsbereichen gibt, sondern nur einen globalen Kontext, in dem alle
Deklarationen in der gesamten DTD sichtbar sind, wird mit einem externen Parameter-Entity der gesamte Text aus der Datei importiert.
Bei Importieren von Modulen müssen die folgenden Regeln für das
mehrfache Vorkommen einer Deklaration beachtet werden:
■
Existieren mehrere Attributlistendeklarationen für ein Element, so
werden diese miteinander verkettet.
Kapitel 3 – Document Type Definitions
■
■
41
Entitäts- und Attributdeklarationen:
Existieren für eine Entität oder ein Attribut mehrere Deklarationen, so
wird jeweils die erste verwendet; alle folgenden werden ignoriert.
Element- und Notationsdeklarationen:
Elemente und Notationen dürfen nicht mehr als einmal deklariert werden; mehrfaches Vorkommen ist ein Gültigkeitsfehler.
3.4.2 Bedingte Abschnitte
Man kann mit einem sog. bedingten Abschnitt bestimmte Teile eines
Moduls oder einer öffentlichen DTD zum Einbinden in anderen DTDs
bzw. zum Ausschluss aus der DTD markieren. Bedingte Abschnitte werden wie folgt deklariert:
<![INCLUDE[
<!-- Die folgenden Deklarationen werden einbezogen-->
<!ELEMENT kreis (x, y, r)>
...
]]>
<![IGNORE[
<!-- Die folgenden Deklarationen werden nicht einbezogen-->
<!ELEMENT rechteck (x1, y1, x2, y2)>
...
]]>
Mit Hilfe von Parameter-Entities kann man in einer DTD bestimmen,
welche Teile aus anderen DTDs übernommen werden. Dazu werden in
der zu importierenden DTD die INCLUDE und IGNORE Abschnitte durch
Entities deklariert. Zum Beispiel:
<!ENTITY % formen "INCLUDE">
<!ENTITY % rechtecke "IGNORE">
<![%formen;[
<!-- Die folgenden Deklarationen werden einbezogen-->
<!ELEMENT kreis (x, y, r)>
...
]]>
<![%rechtecke;[
<!-- Die folgenden Deklarationen werden nicht einbezogen-->
<!ELEMENT rechteck (x1, y1, x2, y2)>
...
]]>
42
Einführung in XML
In einer DTD, die dieses Modul verwendet, können dann die beiden Entities überschrieben werden, womit man bestimmt, ob die Deklarationen für
Rechtecke oder Formen übernommen werden. Im folgenden Beispiel
werden die Standardeinstellungen aus der importierten Datei geometrie.mod vertauscht:
<!ENTITY % formen "IGNORE">
<!ENTITY % rechtecke "INCLUDE">
<!ENTITY % geometrie SYSTEM "geometrie.mod">
%geometrie;
...
Dadurch kann man DTDs in logisch zusammenhängende Blöcke aufteilen
und diese durch Parameter-Entities mit INCLUDE oder IGNORE vorbelegen.
Bevor die DTD importiert wird, kann man diese Entities überschreiben
und so genau bestimmen, welche Blöcke aus dieser DTD verwendet werden. Dies ist ein mächtiges Werkzeug, das besonders beim Erstellen von
öffentlichen DTDs hilfreich ist.
3.4.3 Verwenden der internen Teilmenge
In der Einführung haben wir zwei Formen der DOCTYPE-Deklaration in
einem XML-Dokument kennengelernt:
<?xml version='1.0'?>
<!DOCTYPE rootElement SYSTEM "dateiname.dtd">
und
<?xml version='1.0'?>
<!DOCTYPE rootElement[
interne Deklarationen
]>
Eine weitere Möglichkeit besteht darin, eine externe DTD einzubinden
und zusätzlich eine interne Teilmenge zu spezifizieren:
<?xml version='1.0'?>
<!DOCTYPE rootElement SYSTEM "dateiname.dtd" [
interne Deklarationen
]>
Kapitel 3 – Document Type Definitions
43
In diesem Teil kann man z. B. die Parameter-Entities setzen, um zu
bestimmen, welche Teile aus der importierten DTD verwendet werden.
Zum Beispiel:
<?xml version='1.0'?>
<!DOCTYPE rootElement SYSTEM "script.dtd" [
<!ENTITY % BiBTeXStyle.Modul "INCLUDE">
<!ENTITY % BiBWordStyle.Modul "IGNORE">
]>
3.5
Tipps zum Erstellen von DTDs
Wie Programmcode können unkommentierte und schlecht formatierte
DTDs nur bedingt nachvollzogen werden. Daher die folgenden Tipps:
■
■
Deklarieren Sie logisch zusammenhängende Elemente in voneinander
getrennten Abschnitten.
Verwenden Sie Leerräume, um Ihre DTD zu formatieren und damit
lesbarer zu machen. In DTDs werden Leerzeichen, Tabulatoren und
Newline-Zeichen ignoriert. DTDs wie
<!ATTLIST buch titel CDATA #REQUIRED autor
NMTOKEN #REQUIRED isbn ID #REQUIRED>
sind schwer lesbar. Die folgende äquivalente Deklaration sieht jedenfalls viel besser aus:
<!ATTLIST buch
titel
CDATA
#REQUIRED
autor
NMTOKEN
#REQUIRED
isbn
ID
#REQUIRED
>
■
Kommentieren Sie ihre DTDs, damit andere Benutzer die Deklarationen verstehen. Auch der Autor selbst könnte nach einiger Zeit nur mit
Mühe sein eigenes Werk ohne Kommentare verstehen.
<!-- buch hat ein Titel, Autor und ISBN-Nummer. -->
<!-- Die ISBN ... -->
<!ATTLIST buch ...
■
Versuchen Sie wiederkehrende Teile durch Parameter-Entities zu definieren:
Einführung in XML
44
<!ENTITY % standelements "title, author, year">
<!ELEMENT book (%standelements;, isbn, ...)>
<!ELEMENT article (%standelements;, pages, ...)>
<!ELEMENT thesis (%standelements;, school, ...)>
3.6
Ein Datenbankbeispiel
In diesem Beispiel betrachten wir eine geschachtelte Tabelle, die wir aus
zwei flachen Tabellen Abteilung und Mitarbeiter mit einer 1:n-Beziehung (jeder Mitarbeiter ist in genau einer Abteilung) herstellen.
AID
ABTBEZ
ORT
FE
Forschung&Entwicklung
Dresden
HR
Personalabteilung
Kassel
EC
e-Commerce
?
VB
Vertrieb&Beratung
Dresden
Tab. 3–1 Abteilungen
NAME
MID
GJAHR
AID
Peter
1022
1959
FE
Gabi
1017
1963
HR
Beate
1305
1978
VB
Rolf
1298
1983
HR
Uwe
1172
1978
FE
Tab. 3–2 Mitarbeiter
Kapitel 3 – Document Type Definitions
45
Mithilfe eines Stylesheet (nf2tohtml.xsl) erzeugen wir z. B. die folgende geschachtelte Darstellung:
Abb. 3–1 Geschachtelte Darstellung von Abteilungen und
Mitarbeitern
Eine DTD für die geschachtelte Form wäre offensichtlich (abteilungen.dtd):
<!-- DTD fuer Abteilung und Mitarbeiter -->
<!ENTITY % nf2types.mod SYSTEM "nf2types.mod">
%nf2types.mod;
<!ELEMENT ABTEILUNGEN (ABTEILUNG*)>
<!ATTLIST ABTEILUNGEN
%settype;
%idtype;>
<!ELEMENT ABTEILUNG (AID, ABTBEZ, ORT, MITARBEITER)>
<!ATTLIST ABTEILUNG
%tupletype;
%idtype;>
<!ELEMENT
<!ATTLIST
<!ELEMENT
<!ATTLIST
<!ELEMENT
<!ATTLIST
AID %atomictype;>
AID %idtype;>
ABTBEZ %atomictype;>
ABTBEZ %idtype;>
ORT %atomictype;>
ORT %idtype;>
<!ELEMENT MITARBEITER (MITARB*)>
<!ATTLIST MITARBEITER
Einführung in XML
46
%settype;
%idtype;>
<!ELEMENT MITARB (NAME, MID, GJAHR)>
<!ATTLIST MITARB
%tupletype;
%idtype;>
<!ELEMENT
<!ATTLIST
<!ELEMENT
<!ATTLIST
<!ELEMENT
<!ATTLIST
NAME %atomictype;>
NAME %idtype;>
MID %atomictype;>
MID %idtype;>
GJAHR %atomictype;>
GJAHR %idtype;>
Wobei das Modul nf2types.mod in diese DTD eingebunden wurde:
<!ENTITY
<!ENTITY
<!ENTITY
<!ENTITY
<!ENTITY
%
%
%
%
%
settype "nf2type CDATA #FIXED 'sett'">
listtype "nf2type CDATA #FIXED 'listt'">
tupletype "nf2type CDATA #FIXED 'tuplet'">
atomictype "(#PCDATA)">
idtype "id ID #REQUIRED">
Ein Beispieldokument nach dieser DTD wäre (abteilungen.xml):
<?xml version='1.0' standalone="no" ?>
<?xml-stylesheet type="text/xsl" href="nf2tohtml.xsl"?>
<!DOCTYPE ABTEILUNGEN SYSTEM "abteilungen.dtd">
<ABTEILUNGEN id="RID-4000" >
<ABTEILUNG id="RID-4001">
<AID id="RID-4003">FE</AID>
<ABTBEZ id="RID-4004">
Forschung&Entwicklung
</ABTBEZ>
<ORT id="RID-4005">Dresden</ORT>
<MITARBEITER id="RID-4006">
<MITARB id="RID-4007">
<NAME id="RID-4008">Peter</NAME>
<MID id="RID-4009">1022</MID>
<GJAHR id="RID-4010">1959</GJAHR>
</MITARB>
<MITARB id="RID-4011">
<NAME id="RID-4012">Uwe</NAME>
<MID id="RID-4013">1172</MID>
<GJAHR id="RID-4014">1978</GJAHR>
</MITARB>
Kapitel 3 – Document Type Definitions
</MITARBEITER>
</ABTEILUNG>
<ABTEILUNG id="RID-4020">
<AID id="RID-4021">HR</AID>
<ABTBEZ id="RID-4022">
Personalabteilung
</ABTBEZ>
<ORT id="RID-4023">Kassel</ORT>
<MITARBEITER id="RID-4024">
<MITARB id="RID-4025">
<NAME id="RID-4026">Gabi</NAME>
<MID id="RID-4027">1017</MID>
<GJAHR id="RID-4028">1963</GJAHR>
</MITARB>
<MITARB id="RID-4029">
<NAME id="RID-4030">Rolf</NAME>
<MID id="RID-4031">1298</MID>
<GJAHR id="RID-4032">1983</GJAHR>
</MITARB>
</MITARBEITER>
</ABTEILUNG>
<ABTEILUNG id="RID-4040">
<AID id="RID-4041">EC</AID>
<ABTBEZ id="RID-4042">
e-Commerce
</ABTBEZ>
<ORT id="RID-4043">?</ORT>
<MITARBEITER id="RID-4044">
</MITARBEITER>
</ABTEILUNG>
<ABTEILUNG id="RID-4060">
<AID id="RID-4061">VB</AID>
<ABTBEZ id="RID-4062">
Vertrieb&Beratung
</ABTBEZ>
<ORT id="RID-4063">Dresden</ORT>
<MITARBEITER id="RID-4064">
<MITARB id="RID-4065">
<NAME id="RID-4066">Beate</NAME>
<MID id="RID-4067">1305</MID>
<GJAHR id="RID-4068">1978</GJAHR>
</MITARB>
</MITARBEITER>
</ABTEILUNG>
</ABTEILUNGEN>
47
49
4 XML Schema
Im vorigen Kapitel haben wir die Möglichkeit kennengelernt, ein XMLDokument mit Hilfe einer DTD zu beschreiben. Die Nachteile sind:
■
■
■
DTDs unterstützen keine Namensräume.
DTDs haben eine sehr beschränkte Anzahl an Datentypen. Z. B. gibt
es keine Datentypen für Zahlen, Datum etc.
DTDs sind selbst keine XML-Dokumente.
XML Schema (kurz XSchema) ist ein Standard des W3C, der ursprünglich DTDs ersetzen sollte. XSchema selbst ist eine XML-basierte Sprache, wodurch die Verarbeitung von XSchema-Dokumenten mit den gleichen Werkzeugen wie für XML-Dokumente erfolgen kann (Parser, DOMSchnittstelle etc.).
Mit XSchema wird also ein in sich abgeschlossenes System geschaffen; es ist sogar möglich, ein XSchema für beliebige XSchema-Dokumente anzugeben, ein sog. Metaschema, das alle Schemadokumente
inklusive sich selbst beschreibt.
Die Standardisierung erfolgt in zwei Teilen: XML Schema Part 1:
Structures [22] und XML Schema Part 2: Datatypes [23]. Diese beiden
Quellen sind schwer zu verstehen. Der sog. Primer [8] enthält eine übersichtliche Einführung.
XSchema ist sehr umfangreich. Wir behandeln hier nur die Grundlagen. Für weitere Details siehe [24].
Einführung in XML
50
4.1
Grundlagen
XSchema-Dokumente haben üblicherweise die Endung *.xsd. Andere
Endungen können ebenfalls verwendet werden. Üblich ist natürlich auch
*.xml! Als Namensraum wird http://www.w3.org/2001/XMLSchema
benutzt. Üblicherweise werden die Abkürzungen xsd oder xs verwendet.
Ein XSchema hat den folgenden Aufbau:
<?xml version='1.0'?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation xml:lang="de-DE">
Dokumentation über das Schema
</xsd:documentation>
</xsd:annotation>
<xsd:element name="x" type="xType"/>
...
</xsd:schema>
Der Element xsd:annotation ist optional und dient zur Angabe von allgemeinen Informationen über das Schema.
Wenn man Namensräume verwendet, kann man durch die Angabe des
Attributes xsi:schemaLocation für das Wurzelelement auf das verwendete Schema verweisen. Verwendet man keine Namensräume so wird das
Attribut xsi:noNamespaceSchemaLocation verwendet. Beispiel:
<document
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:noNamespaceSchemaLocation='mydocument.xsd'>
...
</document>
Ein Dokument, das ein Attribut schemaLocation verwendet, folgt hier.
<p:Person xmlns:p="http://contoso.com/People"
xmlns:v="http://contoso.com/Vehicles"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://contoso.com/People
http://contoso.com/schemas/people.xsd
http://contoso.com/Vehicles
http://contoso.com/schemas/vehicles.xsd">
Kapitel 4 – XML Schema
51
<name>John</name>
<age>28</age>
<height>59</height>
<v:Vehicle>
<color>Red</color>
<wheels>4</wheels>
<seats>2</seats>
</v:Vehicle>
</p:Person>
Ein XML-Dokument, das nach einem XSchema aufgebaut ist, wird als
Instanzdokument dieses Schemas bezeichnet. Als erstes Beispiel betrachten wir ein Schema für das Dokument teilnehmer0.xml aus Kapitel 1
(teilnehmer0.xsd):
<?xml version='1.0'?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="TeilnehmerS"
type="TeilnehmerSType"/>
<xsd:complexType name="TeilnehmerSType">
<xsd:sequence>
<xsd:element name="Teilnehmer"
type="TeilnehmerType"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="vorlesung" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="TeilnehmerType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="vorname" type="xsd:string"/>
<xsd:element name="semester"
type="xsd:positiveInteger"/>
<xsd:element name="fachb" type="xsd:string"/>
</xsd:sequence>
<xsd:attribute name="matrNr"
type="xsd:positiveInteger"/>
</xsd:complexType>
</xsd:schema>
52
Einführung in XML
4.1.1 Deklaration von Elementen
Elemente werden mit <xsd:element> deklariert. Man kann die Typdefinition eines Elements in der Deklaration angeben (anonyme Typdefinition)
oder einen Typnamen angeben. Der Typname muss entweder vordefiniert
sein (z. B. xsd:string) oder im Schema weiter ausgeführt sein:
<xsd:element name="elementName" type="typName"/>
...
<xsd:complexType name="typName">
Definition von typName
</xsd:complexType>
oder
<xsd:element name="elementName">
<xsd:complexType>
anonyme Typdefinition
</xsd:complexType>
</xsd:element>
Dabei sind die Deklarationen innerhalb einer anonymen Typdefinition nur
innerhalb dieses Bereichs und seiner Unterelemente sichtbar, nicht aber in
Elementen, die außerhalb der Deklaration liegen. Zum Beispiel deklariert
die folgende Zeile ein Element TeilnehmerS vom Typ TeilnehmerSType, wobei dieser Typ später im Schema definiert wird:
<xsd:element name="TeilnehmerS"
type="TeilnehmerSType"/>
Äquivalent könnte man schreiben:
<xsd:element name="TeilnehmerS">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Teilnehmer"
type="TeilnehmerType"
minOccurs="0"
maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="vorlesung" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
Kapitel 4 – XML Schema
53
Häufigkeitseinschränkungen bei Elementen
bekommt zwei Attribute, die festlegen, wie oft ein Element vorkommen darf. Mit xsd:minOccurs wird das Minimum, mit
xsd:maxOccurs das Maximum festgelegt. Für optionales Vorkommen
setzt man minOccurs auf 0. Für beliebiges Vorkommen dient das Schlüsselwort unbounded. Sonst sind beliebige Integerwerte zugelassen. Standard ist bei beiden Attributen 1. Zum Beispiel ist Teilnehmer im folgenden Beispiel optional, darf aber beliebig oft vorkommen:
<xsd:element>
<xsd:element name="Teilnehmer" type="TeilnehmerType"
minOccurs="0" maxOccurs="unbounded"/>
4.1.2 Attributdeklaration
Attribute für ein Element werden innerhalb der Definition seines Typs
deklariert. Z. B. deklarieren wir für das Element TeilnehmerS das Attribut vorlesung vom Typ xsd:string wie folgt:
<xsd:complexType name="TeilnehmerSType">
...
<xsd:attribute name="vorlesung" type="xsd:string"/>
</xsd:complexType>
Attribute dürfen nur sog. einfache Datentypen (simple types) haben.
Häufigkeitseinschränkungen bei Attributen
Ein Attribut tritt in einem Element genau einmal oder garnicht auf. Angaben, ob ein Attribut optional, verpflichtend oder fest vorgegeben ist, werden in der Attributdefinition <xsd:attribute> durch die Besetzung der
folgenden drei Attribute geregelt:
■ use,
■ default
und
■ fixed.
Das Attribut use von <xsd:attribute> kann die Werte required,
optional oder sogar prohibited (verboten!) annehmen.
Einführung in XML
54
Mit default kann ein Standardwert angegeben werden. Dabei macht
die Angabe eines Standardwerts nur bei use='optional' Sinn und ist in
XSchema nur in dieser Kombination erlaubt.
Das Attribut fixed wird in Deklarationen von Elementen und Attributen verwendet, um sicherzustellen, dass Elemente oder Attribute einen
bestimmten Wert haben. Die Verwendung von fixed und default in derselben Deklaration ist sinnlos und deshalb verboten. Jede Kombination
aus diesen drei Attributen hat eine festgelegte Bedeutung. Wir geben hier
nur zwei Beispiele an:
<xsd:element name='book'>
<xsd:complexType>
<!-- Das Attribut muss mit dem Wert 'de' vorkommen -->
<xsd:attribute name='lang' type='xsd:string'
use='required' fixed='de'/>
<!-- Optional: Standardwert 'latin' falls nicht
angegeben.-->
<xsd:attribute name='script' type='xsd:string'
use='optional' default='latin'/>
</xsd:complexType>
</xsd:element>
4.1.3 Globale Elemente und Attribute
Globale Element- und Attribut-Deklarationen treten als Kind des Wurzelelements xsd:schema auf. Globale Deklarationen sind überall im
Schema sichtbar und können an beliebigen Stellen mit dem Attribut ref
referenziert werden. Globale Elemente haben weiterhin die besondere
Eigenschaft, Wurzelelemente eines Instanzdokuments sein zu können. Sie
haben jedoch die folgenden Einschränkungen:
■
■
Sie müssen einfache wie komplexe Typen direkt benennen. D. h. die
Verwendung des ref-Attributs ist nicht erlaubt (siehe weiter unten).
Die Kardinalität darf in der Deklaration der globalen Elemente nicht
eingeschränkt werden, d.h. die Attribute minOccurs und maxOccurs
dürfen dort nicht auftreten, wohl aber in den Elementen, in denen sie
mit ref verwendet werden.
Beispiel (aus XML Schema Primer):
Kapitel 4 – XML Schema
55
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:po="http://www.example.com/PO1"
targetNamespace="http://www.example.com/PO1">
<element name="purchaseOrder"
type="po:PurchaseOrderType"/>
<element name="shipTo" type="po:USAddress"/>
<element name="billTo" type="po:USAddress"/>
<element name="comment" type="string"/>
<element name="name" type="string"/>
<element name="street" type="string"/>
<complexType name="PurchaseOrderType">
<sequence>
<element ref="po:shipTo"/>
<element ref="po:billTo"/>
<element ref="po:comment" minOccurs="0"/>
<!-- etc. -->
</sequence>
</complexType>
<complexType name="USAddress">
<sequence>
<element ref="po:name"/>
<element ref="po:street"/>
<!-- etc. -->
</sequence>
</complexType>
<!-- etc. -->
</schema>
Namenskonflikte
Es stellt sich die Frage: Was passiert, wenn man zwei Dingen denselben
Namen gibt? Ein Konflikt liegt vor, wenn zwei Typen mit demselben
Namen definiert werden. Dagegen liegt kein Konflikt vor, wenn:
■
■
ein Element und ein Typ mit demselben Namen definiert werden.
zwei Elemente innerhalb verschiedener Datentypdefinitionen denselben Namen haben. Z. B. ein title-Element innerhalb einer bookType-Definition und einer personType-Definition mit unterschiedlichem Inhalt bei beiden Angaben (man nennt solche Deklarationen
auch lokale Element-Deklarationen).
Einführung in XML
56
■
ein vordefinierter Datentyp und ein neu definierter Datentyp denselben
Namen haben (da vordefinierte Datentypen zum XSchema-Namensraum gehören).
4.2
Vordefinierte Datentypen
Abb. 4–1 wurde dem Standard entnommen und enthält alle vordefinierten
Datentypen und ihre Ableitungshierarchie in XSchema. Wir gehen auf
einige ein.
Abb. 4–1 Vordefinierte Datentypen in XML Schema
Kapitel 4 – XML Schema
57
4.2.1 Strings
■ string
Beschreibt beliebige Zeichenketten, in denen alle Whitespaces1 erhalten bleiben.
■ normalizedString
Hier werden Zeilenumbrüche und Tabulatoren in Leerzeichen umgewandelt.
■ token
Whitespaces werden jeweils zu einem einzigen Leerzeichen zusammengezogen. Whitespaces am Anfang und Ende werden entfernt.
■ language
Akzeptiert alle Abkürzungen für Sprachangaben wie en-US oder deDE.
und NMTOKENS haben die gleiche Bedeutung wie in DTDs. Name
ist ähnlich wie NMTOKEN, darf aber nicht mit einer Ziffer, Bindestrich oder
Punkt beginnen (wie ID in DTDs). NCName ähnlich wie Name, darf aber
keinen Doppelpunkt enthalten (no colon). QName ist ein qualifizierter
Name, der aus einem NCName besteht, dem ein Namensraumpräfix vorangestellt werden kann; zwischen beiden Teilen steht ein Doppelpunkt.
NMTOKEN
Die Datentypen ID, IDREF, IDREFS, ENTITY, ENTITIES und NOTATION
wurden in XML Schema aus DTDs übernommen.
Weiterhin sind die folgenden Datentypen definiert:
dient zur Aufnahme von URI-Angaben.
base64Binary dient zur Aufnahme von binären Daten in Base64Kodierung.
■ anyURI
■
■ hexBinary
für hexadezimal kodierte Daten.
4.2.2 Zahlen
Für ganze Zahlen sind (unter anderem) die folgenden Datentypen vorgesehen:
1. Zeilenumbrüche, Tabulatoren und Leerzeichen.
Einführung in XML
58
■ integer
■ positiveInteger
■ negativeInteger
■ long
■ unsignedLong
Dabei ist zu beachten, dass z. B. integer keine Einschränkung bzgl. der
Länge einer Zahl macht. Von integer sind weitere Datentypen wie unsignedInt, unsignedShort usw. abgeleitet.
Für Gleitkommazahlen ist vorgesehen:
für Gleitkommazahlen beliebiger Länge!
float für 32-Bit-Gleitkommazahlen und
double für 64-Bit-Gleitkommazahlen.
■ decimal
■
■
Weiterhin dient der Typ boolean für Wahrheitswerte. Diese können entweder numerisch (0 oder 1) oder durch vordefinierte Bezeichner (true
oder false) spezifiziert werden.
4.2.3 Zeit und Datum
Für Zeit- und Datumsangaben existieren z. B.: time, dateTime, date,
gYear, gYearMonth, gDay, gMonth und gMonthDay.
Dabei werden bei einigen Datentypen für Zeit- und Datumsangaben
vorhandene Standards unterstützt. Z. B. ist dateTime ein Datum nach
dem ISO 8601 Standard. Das g steht für Gregorianischer Kalender.
4.3
Einfache Datentypen
Einfache Datentypen sind solche, die keine Elemente oder Attribute
bekommen. Andere Datentypen sind komplex.
Man kann einfache Datentypen durch Erweiterung oder Einschränkung
vorhandener Datentypen definieren und somit den Wertebereich eines
Elements ziemlich genau spezifizieren. Die Einschränkung eines einfachen Datentyps neuerTyp geschieht mit:
Kapitel 4 – XML Schema
59
<xsd:simpleType name="neuerTyp">
<xsd:restriction base="basisTyp">
Facetten zur Einschränkung des Basistyps
</xsd:restriction>
</xsd:simpleType>
Datentypen bieten verschiedene Möglichkeiten für die Einschränkung
(sog. Facetten). Diese sind im Standard für jeden Datentyp angegeben.
Die Semantik einer Facette wechselt ggf. von Datentyp zu Datentyp. Es
folgen hier ein paar Beispiele.
4.3.1 Einschränkung des Wertebereichs
Zur Einschränkung eines Wertebereichs dienen die Facetten:
■ xsd:minInclusive,
■ xsd:maxInclusive,
und
xsd:maxExclusive.
■ xsd:minExclusive
■
Beispiel:
<xsd:simpleType name="lottoZahl">
<xsd:restriction base="xsd:integer">
<xsd:minExclusive value="0"/>
<xsd:maxInclusive value="49"/>
</xsd:restriction>
</xsd:simpleType>
4.3.2 xsd:enumeration
Damit wird für verschiedene Datentypen eine Liste von zulässigen Werten angegeben.
<xsd:simpleType name="bundesLaenderType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Baden-Württemberg"/>
<xsd:enumeration value="Bayern"/>
<xsd:enumeration value="Berlin"/>
<xsd:enumeration value="Brandenburg"/>
<xsd:enumeration value="Bremen"/>
<xsd:enumeration value="Hamburg"/>
60
Einführung in XML
<xsd:enumeration value="Hessen"/>
<xsd:enumeration value="Mecklenburg-Vorpommern"/>
<xsd:enumeration value="Niedersachsen"/>
<xsd:enumeration value="Nordrhein-Westfalen"/>
<xsd:enumeration value="Rheinland-Pfalz"/>
<xsd:enumeration value="Saarland"/>
<xsd:enumeration value="Sachsen"/>
<xsd:enumeration value="Sachsen-Anhalt"/>
<xsd:enumeration value="Schleswig-Holstein"/>
<xsd:enumeration value="Thüringen"/>
</xsd:restriction>
</xsd:simpleType>
4.3.3 xsd:length, xsd:maxLength und xsd:minLength
Spezifizieren die Länge einer Zeichenkette. Bei anderen Datentypen
weicht die Bedeutung ab (z. B. Anzahl der Bytes bei base64Binary).
xsd:length legt die Länge fest, während maxLength und minLength die
maximale bzw. minimale Länge angeben. Beispiel:
<xsd:simpleType name="pwdType">
<xsd:restriction base="xsd:string">
<xsd:minLength value="5"/>
<xsd:maxLength value="8"/>
</xsd:restriction>
</xsd:simpleType>
4.3.4 xsd:pattern
Damit kann man einen String (auch für andere Datentypen wie date und
time!) durch einen regulären Ausdruck einschränken. Reguläre Ausdrücke bieten ein sehr mächtiges Werkzeug zur genauen Beschreibung
eines Wertebereichs. Im folgenden Beispiel definieren wir den Typ bookID, der mit dem Buchstaben b beginnen soll, gefolgt von 9 Ziffern,
gefolgt von einer Ziffer oder X, also eine Zeichenkette der Art
b123456789X:
<xsd:simpleType name="bookID">
<xsd:restriction base="xsd:string">
<xsd:pattern value="b[0-9]{9}[0-9X]"/>
</xsd:restriction>
</xsd:simpleType>
Kapitel 4 – XML Schema
61
4.3.5 Erweiterung zu Listentypen
Zusätzlich zu den vordefinierten Listentypen, können Listentypen für vorhandene Datentypen definiert werden. Beispiel:
<xsd:simpleType name="BundesLaenderListe">
<xsd:list itemType="bundesLaenderType"/>
</xsd:simpleType>
Beispiel für ein Element von diesem Typ ist etwa:
<blaender>
Baden-Württemberg Bayern Berlin Brandenburg Bremen
</blaender>
Listentypen können durch Facetten eingeschränkt werden, etwa im folgenden Beispiel für die Länge der Liste (genau sechs Einträge):
<xsd:simpleType name="SechsBundesLaenderListe">
<xsd:restriction base="BundesLaenderListe">
<xsd:length value="6"/>
</xsd:restriction>
</xsd:simpleType>
4.3.6 Erweiterung durch Vereinigung
Mit xsd:union können mehrere einfache Datentypen zu einem neuen
Datentyp vereinigt werden.
Im folgenden Beispiel kann die Angabe eines Bundeslandes entweder
durch den Namen oder durch eine Postleitzahl aus diesem Bundesland
erfolgen:
<xsd:simpleType name="postleitzahlType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[0-9]{5}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="land_oder_plzType">
<xsd:union
memberTypes="postleitzahlType bundesLaenderType"/>
</xsd:simpleType>
Einführung in XML
62
Dabei bekommt das Attribut memberTypes eine Liste der Datentypen, aus
denen der zu definierende Datentyp bestehen soll.
4.4
Komplexe Datentypen
Komplexe Datentypen sind solche, die Elemente oder Attribute haben.
Sie werden mit der Anweisung xsd:complexType definiert:
<xsd:complexType name="typeName">
Deklaration der Elemente
Deklaration der Attribute
</xsd:complexType>
Komplexe Typen können wiederum komplexen oder aber einfachen Inhalt
haben (complex content, simple content).
4.4.1 Definition von einfachem Inhalt
Ein Element hat einen komplexen Typ mit einfachem Inhalt, wenn für das
Element Attribute definiert werden, der Inhalt aber einfach ist (kein
Markup). Beispiel:
<name anrede="Herr">Lehmann</name>
Zur Definition von solchen Datentypen kommt innerhalb von xsd:complexType die Anweisung xsd:simpleContent. Innerhalb von
xsd:simpleContent wird ein einfacher Datentyp durch xsd:extension
um die erlaubten Attribute erweitert. Beispiel:
<xsd:element name="name" type="nameType"/>
<xsd:complexType name="nameType">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="anrede" type="xsd:string"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
Für diese gängige Kombination ist die Deklaration in XML-Schema leider sehr langatmig.
Kapitel 4 – XML Schema
63
4.4.2 Definition von komplexem Inhalt
Innerhalb der Definition (xsd:complexType) werden die Elemente und
Attribute angegeben, die der zu definierende Typ enthalten darf oder
muss, das sog. Contentmodell. Zusätzlich zu der Möglichkeit zur Festlegung der Häufigkeit (die Attribute minOccurs und maxOccurs von
xsd:element) kann die Reihenfolge der Elemente festgelegt werden.
Dazu existieren die folgenden drei Unterelemente von xsd:complexType:
■ <xsd:sequence>
Die angegebene Reihenfolge ist zwingend. Als Beispiel betrachten wir
die Definition von AbteilungType zur Beschreibung einer Abteilung
aus dem vorigen Kapitel:
<xsd:complexType name="AbteilungType">
<xsd:sequence>
<xsd:element name="AID" type="atomicType"/>
<xsd:element name="ABTBEZ" type="atomicType"/>
<xsd:element name="ORT" type="atomicType"/>
<xsd:element name="MITARBEITER"
type="MitarbeiterType"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required"/>
<xsd:attribute name="nf2type" type="nf2typeType"
use="optional" fixed="tuplet"/>
</xsd:complexType>
Hier ist die Reihenfolge vorgeschrieben. Die Angabe im Instanzdokument:
<ABTEILUNG id="RID-4001">
<AID id="RID-4003">FE</AID>
<ORT id="RID-4005">Dresden</ORT>
<ABTBEZ id="RID-4004">
Forschung&Entwicklung
</ABTBEZ>
...
</ABTEILUNG>
wäre damit ungültig, da ORT und ABTBEZ vertauscht wurden.
Einführung in XML
64
■ <xsd:choice>
Mit choice wählt man Elemente aus einer Gruppe. Durch Angabe von
minOccurs (Vorgabe 1, Werte >= 0) und maxOccurs (Vorgabe 1, Werte
>= 0, unbounded für beliebig häufiges Auftreten) kontrolliert man die
Anzahl der Auswahlschritte.
Im folgenden einfachen Beispiel hat ein Buch entweder ein authorsoder author-Element:
<xsd:complexType name="bookType">
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:choice>
<xsd:element name="authors"
type="authorsType"/>
<xsd:element name="author"
type="authorType"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
In dem Beispiel sehen wir auch, dass choice neben anderen Elementen in einer Sequenz auftauchen darf. Das ist auch der Fall im folgenden HausTyp.
<xsd:complexType name="HausTyp">
<xsd:sequence>
<xsd:element name="Lage" type="xsd:string"/>
<xsd:element name="Grundstueck"
type="GrundstueckT"/>
<xsd:choice minOccurs="1" maxOccurs="100">
<xsd:element name="Wohnen" type="WohnenT"/>
<xsd:element name="Praxis" type="PraxisT"/>
</xsd:choice>
<xsd:element name="Bemerkung" type="xsd:string"/>
</xsd:sequence>
<xsd:attribute name="Preis"
type="xsd:positiveInteger"/>
</xsd:complexType>
Kapitel 4 – XML Schema
65
Er erlaubt in einem Haus bis zu 100 Wohnungen und Praxen (auch
gemischt). In einer DTD würde die Angabe (ohne die nicht formulierbare Beschränkung auf 100 Wohn-/Praxiseinheiten) wie folgt lauten:
<!ELEMENT Haus (Lage, Grundstueck, (Wohnen | Praxis)+, Bemerkung)>
Neben dem Auftreten innerhalb eines sequence-Elements kann
choice wiederum Unterelement von choice sein, d. h. eine Schachtelung ist erlaubt, allerdings nicht in Verbindung mit dem folgenden
all-Element.
■ <xsd:all>
Die mit all geklammerten Unterelemente dürfen in beliebiger Reihenfolge, jedes davon höchstens einmal, vorkommen. Für die Attribute minOccurs und maxOccurs der element-Elemente innerhalb von
all sind jeweils die Werte 0 und 1 zulässig. Sollen Unterelemente
auch fehlen dürfen, muss bei den zugehörigen element-Elementen
explizit minOccurs="0" gesetzt werden, der Vorgabewert ist 1.
Für die Attribute minOccurs und maxOccurs des Elements all gilt:
für minOccurs sind die Werte 0 und 1 erlaubt, für maxOccurs ist nur
der Vorgabewert 1 zulässig, sodass man maxOccurs üblicherweise
nicht angeben wird.
Als Vaterelemente für all können dienen: group, complexType,
restriction (sowohl simpleContent als auch complexContent),
extension (sowohl simpleContent als auch complexContent),
nicht zulässig sind all, choice und sequence, weil dies zu Mehrdeutigkeiten führen könnte.
Beispiele:
<xsd:complexType name="authorType">
<xsd:all>
<xsd:element name="nachname" type="xsd:string" />
<xsd:element name="vorname" type="xsd:string" />
</xsd:all>
</xsd:complexType>
authorType
Reihenfolge.
verlangt genau einen Vor- und Nachnamen in beliebiger
Einführung in XML
66
<xsd:complexType name="authorType">
<xsd:all>
<xsd:element name="nachname" type="xsd:string"
minOccurs="0"/>
<xsd:element name="vorname" type="xsd:string"
minOccurs="0"/>
</xsd:all>
</xsd:complexType>
Wieder können Vor- und Nachname in beliebiger Reihenfolge erscheinen, jeder höchstens einmal oder auch gar nicht.
4.4.3 Erweiterung von komplexen Elementen
Ähnlich der Einschränkung von einfachen Datentypen, können neue komplexe Datentypen durch Erweiterung oder Einschränkung definiert werden. Hierzu dienen die Anweisungen xsd:extension und xsd:restriction:
<xsd:complexType name='typName'>
<xsd:complexContent>
Einschränkung oder Erweiterung
</xsd:complexContent>
</xsd:complexType>
Die Erweiterung von komplexen Typen mit komplexem Inhalt basiert auf
der Zusammenfügung von Gruppen von Elementen und Attributen zu
einer neuen Gruppe, die den Inhalt eines Datentyps definiert. Zur Erweiterung dient xsd:extension. In dem Beispiel erweitern wir den authorType um die Angabe einer Anrede:
<xsd:complexType name='newauthorType'>
<xsd:complexContent>
<xsd:extension base="authorType">
<xsd:sequence>
<xsd:element name="anrede" type="anredeType"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Kapitel 4 – XML Schema
67
Die
Einschränkung von komplexen Datentypen geschieht mit
xsd:restriction, ist aber nicht sinnvoll zu verwenden, da der Datentyp
im Prinzip neu definiert werden muss. Wir geben trotzdem ein Beispiel:
<xsd:complexType name='personType'>
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="GebDatum" type="xsd:date"/>
<xsd:element name="Beruf" type="berufType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name='authorType'>
<xsd:complexContent>
<xsd:restriction base="personType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="GebDatum" type="xsd:date"/>
</xsd:sequence>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
4.4.4 Gemischter Inhalt
Treten im XML-Dokument Elemente und „normaler“ Text gemischt auf,
etwa wie in
<Anrede>
Sehr geehrte Frau, sehr geehrter Herr
<Nachname>Meier</Nachname>
!
</Anrede>
dann spricht man von gemischtem Inhalt. In XML Schema setzt man
dafür dann das optionale Attribut mixed (default false) auf true:
<xsd:element name="Anrede">
<xsd:complexType mixed="true">
<xsd:sequence>
<xsd:element name="Nachname" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
Einführung in XML
68
Man beachte aber, dass XML Schema weiterhin bei Verwendung des
Attributs mixed Reihenfolge und Anzahl des Auftretens von Unterelementen genau kontrolliert. Freigestellt ist nur der eingestreute Text, im
Beispiel vor und hinter Nachname, wo genau, lässt sich aber nicht vorschreiben.
4.4.5 anyType
ist der oberste Typ in der XML-Schema-Typhierarchie. Elemente von diesem Typ haben keinerlei Einschränkung bezüglich ihres
Inhalts. Man sollte deshalb vermeiden, Elemente von diesem Typ zu definieren.
anyType
ist der Standardwert für das type-Attribut in Elementdeklaration, darf also weggelassen werden.
anyType
<xsd:element name="irgendwas"/>
ist also äquivalent zu:
<xsd:element name="irgendwas" type="xsd:anyType"/>
4.4.6 Leerer Inhalt
Datentypen für Elemente mit leerem Inhalt werden wie folgt definiert
(abgekürzte Schreibweise):
<xsd:element name="preisInternational">
<xsd:complexType>
<xsd:attribute name="währung" type="xsd:string"/>
<xsd:attribute name="wert" type="xsd:decimal"/>
</xsd:complexType>
</xsd:element>
4.4.7 Referenzieren von Elementen und Attributen
Man kann innerhalb von Typdefinitionen auf Elemente oder Attribute verweisen, um diese zum Inhalt hinzuzufügen. Dazu kann man Elemente, die
häufiger vorkommen, einmal global definieren und dann an mehreren
Stellen auf diese Typdefinition verweisen. Beispiel:
Kapitel 4 – XML Schema
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string"/>
<xsd:complexType name="bookType">
<xsd:sequence>
<xsd:element ref="title"/>
<xsd:element ref="author"/>
<xsd:element name="isbn" type="isbnType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="articleType">
<xsd:sequence>
<xsd:element ref="title"/>
<xsd:element ref="author"/>
<xsd:element name="journal" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
69
71
5 XSLT und XPath
Nachdem wir die Grundlagen XML-basierter Sprachen und der XMLDokumente gelernt haben, wenden wir uns der ersten Anwendungsmöglichkeit für solche Sprachen zu, der Umwandlung in andere Dokumente
oder andere Sprachen mittels XSLT-Stylesheets. Dabei ist XSLT ein Teil
der Extensible Stylesheet Language (XSL). XSL besteht aus drei Teilen
(vergl. Kapitel 1):
■
■
■
Formatting Objects (kurz fo),
XPath [12] und
XSLT [10] (XSL-Transformationen).
In diesem Kapitel behandeln wir XSLT und XPath.
5.1
XSLT als XML-Dokument
XSLT-Stylesheet-Dateien haben meistens die Endung „.xsl“. XSLT-Stylesheets sind XML-Dokumente und beginnen deshalb mit der XMLInstruktion:
<?xml version='1.0' ...?>
XSLT verwendet den Namensraum http://www.w3.org/1999/XSL/
Transform. Meistens wird dafür die Abkürzung „xsl:“ verwendet.
Das Rootelement eines Stylesheets ist <xsl:stylesheet>. Alternativ
kann man <xsl:transform> verwenden. Wichtige Attribute des Rootelements sind:
Einführung in XML
72
■ version
Die Version eines Stylesheets. Wir verwenden die Version 1.0.1
■ xmlns:xsl
Angabe des Namensraums.
■ id
Damit kann eine eindeutige ID für das Stylesheet vergeben werden.
Aus einem XML-Dokument wird wie folgt auf ein Stylesheet, z. B. auf
mystylesheet.xsl, verwiesen:
<?xml-stylesheet type='text/xsl' href='mystylesheet.xsl'?>
5.1.1 Erstes Beispiel: Hallo Welt
Bevor wir in die Details einsteigen, schauen wir uns ein sehr einfaches
Beispiel an (hallo.xml):
<?xml version='1.0' standalone='yes'?>
<?xml-stylesheet type='text/xsl' href='hallo.xsl'?>
<greeting>
Hallo Welt!
</greeting>
Die Datei hallo.xsl enthält das folgende Stylesheet, das dieses XMLDokument in HTML transformiert:
<?xml version='1.0' encoding='ISO-8859-1'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:template match='/'>
<html>
<head>
<title>Beispiel</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match='greeting'>
1. XSLT Version 2.0 erschien 2007 als W3C Recommendation.
Kapitel 5 – XSLT und XPath
73
<h1><xsl:value-of select='.'/></h1>
</xsl:template>
</xsl:stylesheet>
Abb. 5–1 Darstellung von „hallo.html“ mit Internet Explorer 6
5.2
XPath-Datenmodell
Grundlage für die Transformierung bildet das XPath-Datenmodell. Ein
XML-Dokument wird in diesem Datenmodell zu einem Baum. Eine
Besonderheit ist, dass die Wurzel des Baums im Datenmodell nicht dem
Rootelement des XML-Dokuments entspricht, sondern als abstrakter
Knoten eine Stufe höher liegt. Der Baum enthält sieben unterschiedliche
Arten von Knoten:
■
Element
Ein Element kann andere Elemente und zusätzlich jeden anderen Knotentyp mit Ausnahme des Wurzelelements enthalten.
74
■
■
■
■
Einführung in XML
Attribut
Attribute haben keine weiteren Kinder. Sie werden als Blatt-Knoten
dargestellt.
Text
Texte sind immer Kinder anderer Knoten. In einem Element können
beliebig viele Textknoten vorkommen.
Kommentar
Kommentare bilden in XPath auch eigene Baumknoten.
Steuerungsanweisung
Steuerungsanweisungen werden in XPath auch als Baumknoten
betrachtet.
■
Namensraum
Namensräume werden als besondere Knoten betrachtet, da sie über das
ganze Dokument Bedeutung haben.
■
Wurzel
Der Wurzelknoten ist ein abstrakter Knoten über dem Wurzelelement.
Er enthält das ganze Dokument mit allen Deklarationen vor dem Wurzelelement.
Wir schauen uns ein einfaches Dokument an und betrachten den zugehörigen Baum:
<?xml version='1.0' encoding='ISO-8859-1'?>
<!-- So könnten die nächsten E-Mails aussehen! -->
<nachricht>
<?interpbilder?>
<subj>XML-Dokumente als Bäume</subj>
<text format="text">heute haben wir gelernt ...</text>
</nachricht>
Abb. 5–2 enthält alle Knotentypen in diesem Datenmodell. Diese Baumsicht ist für das Verständnis von XSLT von zentraler Bedeutung. Man
bezeichnet das Eingabedokument als Quellbaum und das Ausgabedokument als Ergebnisbaum. Der Stylesheet-Prozessor (als Teil eines Browsers oder getrennt wie Xalan) durchläuft nun den XML-Baum (Quellbaum) und versucht, an jedem Knoten eine passende Regel zu finden,
wobei bei mehreren passenden die spezifischste (genauest passende,
Kapitel 5 – XSLT und XPath
75
mehr dazu später) genommen wird. Der Inhalt des <xsl:template>-Elements bestimmt, welche Ausgabe produziert wird.
...
Abb. 5–2 Baumdarstellung eines Dokuments
5.3
Funktionsprinzip von Stylesheets
Wie bei anderen Stylesheet-Sprachen besteht ein XSLT-Stylesheet aus
Regeln (<xsl:template>), die zu Elementen passen. Jede Regel
beschreibt, welche Ausgabe produziert werden soll, bzw. welche weiteren
Anweisungen ausgeführt werden. Zum Beispiel besagt die Regel
<xsl:template match='greeting'>
<h1><xsl:value-of select="."/></h1>
</xsl:template>
dass aus dem Element greeting im Eingabedokument, die Ausgabe
<h1><xsl:value-of select="."/></h1>
zu erzeugen ist, wobei value-of den Wert eines Knotens liefert, hier
einen Text, und zwar für den ausgewählten Knoten „.“, d. h. den aktuellen
Knoten.
Einführung in XML
76
Innerhalb eines Templates werden weitere Regeln auf Sohnelemente
mit <xsl:apply-templates
select='node-set-expression'/>
angewandt. Dabei kann man mit dem optionalen Attribut select die zu
verarbeitenden Knoten bestimmen. Standardmäßig werden alle Unterknoten genommen. Im folgenden Beispiel wird innerhalb des Templates
für die Wurzel <xsl:apply-templates/> aufgerufen, um die Unterknoten zu verarbeiten:
<xsl:template match='/'>
<html>
<head>
<title>Beispiel</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
Abb. 5–3 Quell- und Zielbaum
Die XSLT-Transformation beginnt mit einer aktuellen Knotenliste, die
einen einzigen Eintrag enthält: den Wurzelknoten (/). Die Verarbeitung
geht dann rekursiv wie folgt weiter:
Kapitel 5 – XSLT und XPath
■
■
■
77
Für jeden Knoten X in der aktuellen Knotenliste sucht der Prozessor
nach allen Templates, die potentiell auf diesen Knoten passen. Aus
dieser Liste wird die passendste Regel genommen.
Das ausgewählte Template wird mit diesem Knoten ausgeführt. Der
erzeugte Teilbaum wird als Teilbaum des Knotens im Zielbaum an der
Stelle eingefügt, wo <xsl:apply-templates> aufgerufen wurde.
Wenn das Template ein Element <xsl:apply-templates> enthält, so
wird eine neue aktuelle Knotenliste aus den Söhnen des aktuellen Knotens erzeugt, und der Prozess wiederholt sich.
Man kann <xsl:template> und <xsl:apply-templates> mit Methodendefinition und Methodenaufruf vergleichen.
5.4
Einfache XSLT-Elemente
Wir betrachten erst ein paar einfache XSLT-Elemente, die in der Regel für
die Erstellung von einfachen Stylesheets ausreichen.
5.4.1 xsl:template und xsl:apply-templates
<xsl:template match='pattern'> ... </xsl:template>
<xsl:apply-templates select='node-set-expression'/>
Die <xsl:template>-Elemente sind die eigentlichen Verarbeitungsregeln. Das im Starttag angegebene Muster pattern bestimmt, auf welche
XML-Elemente das Template angewandt wird. Dabei bestehen solche
pattern aus sog. Lokalisierungspfaden, die eine Untermenge der XPathAusdrücke (expressions) bilden. Wie die Ausgabeformatierung aussehen
soll, steht zwischen Start- und Endtag:
<xsl:template match='/'>
<html>
<head>
<title>Beispiel</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
Einführung in XML
78
Wichtig an diesem Beispiel ist auch die Anweisung <xsl:apply-templates/>, die den Prozessor zwingt, im zu formatierenden Element alle
auftretenden Sohnelemente ebenfalls dem Template-Matching zu unterziehen.
5.4.2 xsl:value-of
<xsl:value-of select='expression'/>
dient zur Ausgabe von Knotenwerten. Das Attribut
select bekommt als Wert einen XPath-Ausdruck, der den auszugebenden Wert berechnet.
<xsl:value-of>
Der Wert eines Elements besteht aus allen geparsten Texten im Element zusammen mit den Werten der Nachfolgerelemente (rekursiv!).
Anders ist es, wenn der Knoten, der gerade zur Verarbeitung ansteht oder
für den die <xsl:value-of>-Formatieranweisung gilt, kein Element ist,
sondern ein Attribut, ein Namensraum, eine Prozessorinstruktion oder ein
Kommentar. Dann ist der Wert, der ausgegeben wird, der Attributwert, die
URI für den Namensraum, der Datenteil (ohne Target und ohne „<?“ und
„?>“) der Prozessorinstruktion bzw. der Text des Kommentars ohne „<!-“ und „-->“.
ist nicht nur für die Ermittlung von Knotenwerten
nützlich, sondern auch für Berechnungen. In dem folgenden Beispiel
geben wir die Anzahl der Teilnehmer aus einer Teilnehmerliste aus (vergl.
Beispiel aus Kapitel 1):
<xsl:value-of>
<xsl:template match='TeilnehmerS'>
<p>Gesamt: <xsl:value-of select='count(//Teilnehmer)'/>
Teilnehmer</p>
<table>
...
</table>
</xsl:template>
5.5
Auswahl von Knoten
Die Wahl der zu verarbeitenden Knoten wird durch XPath-Ausdrücke
angegeben. Einige Elemente bekommen einen sog. Lokalisierungspfad,
Kapitel 5 – XSLT und XPath
79
eine Untermenge der XPath-Ausdrücke. Im Standard wird von pattern
und expression gesprochen. Das Attribut match des Elements <xsl:template> bekommt z. B. einen Lokalisierungspfad als Wert (pattern), dagegen erhält das Attribut select von <xsl:value-of> einen XPath-Ausdruck (expression).
5.5.1 Lokalisierungspfade
Einige der Beispiele werden sich auf das folgende Dokument beziehen
(kunden.xml):
<?xml version='1.0' encoding='ISO-8859-1'?>
<kunden>
<kunde id='0001'>
<titel>Dipl.-Ing.</titel>
<name>Müller</name>
<vorname>Udo</vorname>
<adresse>
<strasse>Lindenstraße</strasse>
<hnr>12</hnr>
<plz>56001</plz>
<ort>Köln</ort>
</adresse>
<wunschliste>
<buecher>
<buch id='4711'>
<titel>Die Zauberflöte</titel>
</buch>
<buch id='4712'>
<titel>Die Schachnovelle</titel>
</buch>
</buecher>
<cds>
<cd id='6601'>
<titel>Die Zauberflöte</titel>
</cd>
</cds>
</wunschliste>
</kunde>
<kunde id='0002'>
...
</kunde>
<kunde id='0003'>
80
Einführung in XML
...
</kunde>
<kunde id='1000'>
...
</kunde>
</kunden>
Lokalisierungspfade sind XPath-Ausdrücke, die einen oder mehrere Knoten in einem XML-Dokument lokalisieren.
Der Kontext
Ähnlich wie die Angabe eines Verzeichnis- oder Dateinamens im Dateisystem, wird ein XPath-Ausdruck in einem sog. Kontext ausgewertet. Dabei
besteht dieser Kontext im wesentlichen aus dem aktuellen Knoten im
Quellbaum.
Weiterer wichtiger Bestandteil des Kontexts sind zwei Integerwerte,
die sog. Kontextposition und Kontextgröße. Diese sind wichtig, wenn man
eine Gruppe von Elementen verarbeitet. Man könnte z. B. einen XPathAusdruck schreiben, der alle <li>-Elemente in einem Dokument behandelt. Die Kontextgröße wäre hier die Anzahl dieser Elemente, die Kontextposition das gerade zu verarbeitende Element innerhalb dieser Menge.
Absolute und relative Lokalisierungspfade
Man unterscheidet weiterhin zwischen absoluten und relativen Lokalisierungspfaden. Absolute Pfade beginnen mit dem Wurzelknoten, relative
mit dem Kontextknoten:
<!--Relativ: Titel des aktuellen Buches-->
<xsl:template match="buch">
<xsl:value-of select="titel"/>
</xsl:template>
<!--Absolut: Name des ersten Kunden im Dokument -->
<xsl:template match="/">
<xsl:value-of select="/kunden/kunde[1]/name"/>
</xsl:template>
Kapitel 5 – XSLT und XPath
81
Aufbau von Lokalisierungspfaden
Ein Lokalisierungspfad besteht aus einer Reihe von Schritten, die den
Pfad vom Ausgangspunkt immer weiterführen und durch / voneinander
getrennt werden. Ein einzelner Schritt hat drei Teile:
■
■
■
eine Achse, die beschreibt in welche Richtung es geht,
einen Knotentest, der spezifiziert, auf welche Arten von Knoten der
Schritt zutrifft,
einen Satz von optionalen Prädikaten, die also Boolesche Tests darstellen, um Kandidaten auszuwählen.
Die Tabellen 5–1 und 5–2 zeigen Knotenachsen und Knotentest in XPath.
Achsentyp
Bedeutung
self
Der Kontextknoten selbst.
child
Alle direkten Kinder des Kontextknotens.
descendant
Alle Unterknoten des Kontextknotens. D. h. alle
Kinder und Kindeskinder. Allerdings ohne die Attribute und Namensräume.
descendant-or-self
Der Kontextknoten selbst zusätzlich zu descendant.
parent
Vaterknoten
ancestor
Alle Vaterknoten bis zur Wurzel.
ancestor-or-self
Alle Vaterknoten inklusive des Kontextknotens
selbst.
following
Alle Knoten, die dem Kontextknoten auf einer beliebigen Ebene im Dokument folgen.
Einführung in XML
82
Achsentyp
Bedeutung
following-sibling
Wie following, aber auf derselben Ebene (gleicher Vaterknoten).
preceding
Alle Knoten, die dem Kontextknoten auf einer beliebigen Ebene im Dokument vorhergehen.
preceding-sibling
Wie preceding, aber auf derselben Ebene (gleicher Vaterknoten).
attribute
Alle Attributknoten des Kontextknotens.
namespace
Alle Namensraumknoten des Kontextknotens.
.
Abkürzung für self
@
Abkürzung für attribute
..
Abkürzung für parent
//
Abkürzung für /descendant-orself::node(), also beliebiges Vorkommen im
Dokument. Z. B. steht //titel für alle titelElemente im Dokument.
Tab. 5–1 Knotenachsen in XPath
Kapitel 5 – XSLT und XPath
83
Knotentest
Bedeutung
QName
Die Angabe eines Namens (z. B. book)
bedeutet bei einer Attributachse Attribute
mit diesem Namen, in einer Namensraumachse Namensräume mit diesem
Namen, in allen anderen Fällen Elemente mit diesem Namen.
*
In einer Attributachse jedes Attribut, in
einer Namensraumachse jeder Namensraum, in allen anderen Achsen jedes
Element.
node()
Jeder Knoten.
text()
Jeder Textknoten.
processing-instruction()
Jede Steueranweisung.
comment()
Jeder Kommentarknoten.
/
Der Wurzelknoten.
@*
Jedes Attribut.
Tab. 5–2 Knotentests in XPath
Die Angabe eines Pfades kann in der langen Schreibweise in der Form
Achse::Knotentest[Prädikat1]...[PrädikatN]
erfolgen oder in der kurzen Schreibweise, wo die Achsenangabe verkürzt
wird und :: wegfällt. In den meisten Fällen kommt man mit der kurzen
Schreibweise aus.
Einführung in XML
84
Das Weglassen der Achsenangabe ist gleichbedeutend mit child, also
Kinder des aktuellen Knotens. Somit sind titel, ./titel und
child::titel drei äquivalente Pfade für das Element titel des aktuellen Knotens.
Wir geben ein einfaches Beispiel an, bevor wir Prädikate behandeln.
Das im Beispiel enthaltene Element <xsl:text> stellt den Textinhalt in
die Ausgabe.
<!-- Kunden, die direkte Nachfahren des XMLWurzelelements sind -->
<xsl:template match='/kunden/kunde'>
<p>
<!-- Gib deren ID-Attribut aus -->
<xsl:value-of select="@id"/><xsl:text> : </xsl:text>
<!-- Und den Namen -->
<xsl:value-of select="name"/>
</p>
</xsl:template>
Prädikate werden innerhalb eckiger Klammern angegeben und können in
jedem Schritt vorkommen. Jedes Prädikat wird ausgewertet und liefert
einen Booleschen Wert. Jeder Knoten, der den Test in einem Prädikat
besteht (zusätzlich zu Knotentest und Achsenangabe), wird in den Knotensatz einbezogen.
Eine Zahl als Prädikat wählt Knoten, die eine Position gleich dieser
Zahl haben. Zum Beispiel gibt die folgende Zeile den Titel des ersten
Buchs innerhalb eines Elements lit aus:
<xsl:value-of select='/lit/book[1]/titel'/>
Sonst können beliebige XPath-Ausdrücke innerhalb eines Prädikates vorkommen. Dabei werden die folgenden Vergleichsoperatoren verwendet:
Operator
Rückgabe
expr1 = expr2
Wahr, wenn expr1 = expr2 (Zeichenkette oder numerisch).
Kapitel 5 – XSLT und XPath
85
Operator
Rückgabe
expr1 != expr2
ungleich
expr1 < expr2
kleiner
expr1 <= expr2
kleinergleich
expr1 > expr1
größer
expr1 >= expr1
größergleich
Im folgenden Beispiel wählen wir alle Kunden aus Kassel:
<xsl:for-each
select="/kunden/kunde[adresse/ort='Kassel']">
...
</xsl:for-each>
Ebenfalls können die Booleschen Operatoren and, or, not(expr),
true() und false() verwendet werden. Beispiel:
<!--Alle Mengen im Schema -->
<xsl:if test="//xsd:complexType/xsd:attribute
[@name='nf2type' and
@use='optional' and
@default='sett']">
...
</xsl:if>
Wir verweisen zuletzt auf die sog. Attributwertvorlage. Mit
<table border="{@size}"/>
kann man den Wert des Attributs size aus dem Quelldokument als Wert
des Attributs border im Zieldokument setzen.
Einführung in XML
86
Ausdrücke
Innerhalb eines Prädikates können XPath-Funktionen und Operatoren
aufgerufen werden. Wir erwähnen hier einige wichtige. Weitere Operatoren können dem Standard entnommen werden.
■
Knotensätze
Auf Knotensätze können folgende Funktionen angewandt werden:
Funktion
Bedeutung
count(Knotensatz)
Die Anzahl der Knoten im Knotensatz.
sum(Knotensatz)
Summe der numerischen Werte aller Knoten im
Knotensatz.
position()
Die Nummer des Kontextknotens im KontextKnotensatz.
last()
Die Nummer des letzten Knotens im KontextKnotensatz.
current()
Liefert den aktuellen Knoten zurück.
name(Knotensatz?)
Name des ersten Knotens in Dokumentenordnung im angegebenen Knotensatz; fehlt die
Knotensatzangabe wird der Name des Kontextknotens zurückgeliefert.
Im folgenden geben wir z. B. den Namen des letzten Kunden aus:
<xsl:value-of
select='/kunden/kunde[position() = last()]/name'/>
Die Funktion current() bedarf weiterer Erklärung. In den meisten
Fällen sind der Kontextknoten und der aktuelle Knoten identisch und
somit z. B. die folgenden beiden Elemente:
<xsl:value-of select='current()'/>
<xsl:value-of select='.'/>
Kapitel 5 – XSLT und XPath
87
Innerhalb eines Prädikats ist dies jedoch nicht der Fall, weil „.“ in der
Auswertung eines Pfadausdrucks jeweils den gerade erreichten Knoten bezeichnet, sich in der Auswertung also schrittweise ändert. Beispiel:
<xsl:template match='kunde'>
<!-- Alle Kunden, die denselben Titel haben -->
<xsl:for-each
select='//kunde[./titel = current()/titel and
./@id
!= current()/@id]'/>
...
</xsl:for-each>
</xsl:template>
In http://www.w3schools.com/xsl/func_current.asp wird das
folgende Beispiel gegeben:
<xsl:apply-templates select="//cd[@titel=current()/@ref]"/>
das alle cd-Elemente verarbeitet, deren titel-Attribute denselben
Wert haben wie das ref-Attribut des gegenwärtigen Knotens.
Im Unterschied dazu verarbeitet
<xsl:apply-templates select="//cd[@titel=./@ref]"/>
■
alle cd-Elemente, bei denen titel-Attributwert und ref-Attributwert
gleich sind.
Zahlen
Für Zahlen existieren die „gängigen“ Operatoren wie +, -, *, div und
mod. Der folgende Ausdruck trifft z. B. auf alle Personen zu, deren
Position in der Menge kunden gerade ist (jeden zweiten Kunden):
/kunden/kunde[(position() mod 2) = 0]
■
Zeichenketten
Für Zeichenketten betrachten wir die folgenden Funktionen:
Funktion
Rückgabe
concat(String1, String2,
...)
Verkettung der Argumente.
Einführung in XML
88
Funktion
Rückgabe
substring(String, Zahl1,
Zahl2?)
Teilstring von String. Dabei gibt
Zahl1 die Stelle vom Anfang des
Strings, Zahl2 die Länge ab Zahl1
an; falls Zahl2 fehlt, geht der
Teilstring bis zum Ende von String.
substring-after
(String, Suchstring)
Teilstring ab dem Ende Suchstring
bis zum Ende von String. Liefert
eine leere Zeichenkette, falls Suchstring nicht in String vorkommt.
substring-before
(String, Suchstring)
Teilstring vom Anfang von String
bis zum Anfang von Suchstring.
translate
(String, Suchstring,
Ersatzstring)
Liefert String zurück, wobei alle in
Suchstring vorkommenden Zeichen durch Zeichen an der entsprechenden Position in Ersatzstring
ersetzt werden.
Wichtig ist darauf zu achten, dass die Position des ersten Zeichens in
einem String 1 ist (nicht 0). Beispiele aus dem W3C-Standard [12]:
substring("12345", 0, 3) liefert 12
substring-before("1999/04/01","/") liefert 1999
substring-after("1999/04/01","/") liefert 04/01
substring-after("1999/04/01","19") liefert 99/04/01
translate("--aaa--","abc-","ABC") liefert AAA
translate("bar","abc","ABC") liefert BAr
Weiterhin existieren die folgenden Operatoren:
Funktion
Rückgabe
contains(String,
Substring)
Wahr, wenn Substring innerhalb
String enthalten ist.
Kapitel 5 – XSLT und XPath
89
Funktion
Rückgabe
starts-with(String,
Substring)
Wahr, wenn String mit Substring
beginnt.
string-length(String)
Anzahl der Zeichen in String.
Beispiele
Wir geben im folgenden weitere Beispiele, um den Umgang mit XPath zu
verdeutlichen.
Regelangabe für den Wurzelknoten:
<xsl:template match="/"> ... </xsl:template>
Gebe Wert des Kontextknotens aus:
<xsl:value-of select="."/>
Gebe Wert vom Unterknoten name des Kontextknotens aus:
<xsl:value-of select="./name"/>
Alle filename-Elemente im Dokument kursiv ausgeben:
<xsl:template match="filename">
<i><xsl:value-of select='.'/></i>
</xsl:template>
Ausgabe des Namens des Kontextknotens:
<xsl:value-of select='name()'/>
Für beliebige Attribute gebe Attributname und -wert aus:
<xsl:template match='@*'>
<p>
<xsl:text>Attributname: </xsl:text>
<xsl:value-of select="name()"/><br/>
<xsl:text>Attributwert: </xsl:text>
<xsl:value-of select="."/>
</p>
</xsl:template>
Einführung in XML
90
5.5.2 Auflösung von Regelkollisionen
Wir haben schon erwähnt, dass bei mehreren passenden Regeln, die passendste für einen Knoten genommen wird. Der Standard legt dazu fest,
dass es die spezifischste Regel ist und gibt eine Rangfolge zur Berechnung vor, die sog. Priorität. Eine Regel mit einem spezifischen Namen
hat eine höhere Priorität als eine ebenfalls passende Regel mit Wildcard *.
Wenn zum Beispiel kunde/wunschliste/cds der Kontextknoten ist,
dann hat cds Vorrang vor *. Beachte, dass kunde/wunschliste/cds und
kunde/wunschliste/* jedoch gleichwertig sind.
Ebenfalls haben Regeln, die mehr als nur einen Knotennamen enthalten, eine höhere Priorität als solche, die nur aus einem Knotennamen
bestehen. Zum Beispiel hat kunde[adresse/ort='Kassel'] bei dem
passenden Kunden eine höhere Priorität als die allgemeine Regel kunde.
Weiterhin hat kunden/kunde Vorrang vor kunde. Die Regeln
kunde[adresse/ort='Kassel'] und kunden/kunde sind jedoch gleichwertig. Wenn mehrere Regeln die gleiche Priorität besitzen, kann der
XSLT-Prozessor den Konflikt lösen, indem er die zuletzt definierte verwendet.
<xsl:template> bekommt
weiterhin das Attribut priority mit dem
Standardwert 0 und Wertebereich zwischen -1 und 1, mit dem die Priorität
einer Regel explizit gesetzt werden kann (je größer die Zahl, desto höher
die Priorität).
5.5.3 Standardregeln
Wenn für einen Knoten keine Regel aus dem Stylesheet passt, so wird eine
sog. Standardvorlage verwendet. XSLT stellt für jeden Knotentyp eine
Standardregel zur Verfügung:
■
Wurzel
<xsl:template match='/'>
<xsl:apply-templates/>
<xsl:template>
Kapitel 5 – XSLT und XPath
■
91
Element
<xsl:template match='*'>
<xsl:apply-templates/>
<xsl:template>
■
Attribut- und Textnodes
<xsl:template match="text()|@*">
<xsl:value-of select="."/>
</xsl:template>
■
Prozessoranweisungen und Kommentare
Standardregel: ignorieren!
<xsl:template
match="processing-instruction()|comment()"/>
Man sollte aber darauf achten, dass diese Regeln erst zur Ausführung
gebracht werden müssen.
Beispiel: Das einfachste Stylesheet, das alle Texte im Dokument ausgibt
(alleTexte.xsl):
<?xml version='1.0' encoding='ISO-8859-1'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:template match='/'>
<html>
<head>
<title>Template mit Standardregeln</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Die Anwendung dieses Stylesheets auf das zu Beginn von Abschnitt 5.5.1
angegebene Dokument kunden.xml liefert die folgende Ausgabe:
92
Einführung in XML
Abb. 5–4 Anwendung von „alleTexte.xsl“ auf „kunden.xml“
Frage: Werden die Attribute ebenfalls ausgegeben?
Hinweis: Mit <xsl:value-of> werden Elemente mit gemischtem Inhalt
(Inhalt enthält Text und Unterelemente) so ausgegeben, dass die Textanteile dieses Elements und aller Unterelemente in Baumreihenfolge
(genauer: In-Order-Reihenfolge) erscheinen. Gibt es Regeln für die
Unterelemente, kommen diese nicht zur Anwendung, weil ein
<xsl:apply-templates/> fehlt.
Gibt man nur <xsl:apply-templates/> an (wie unten gezeigt), erscheinen der umgebende Text und die Ausgabe der Unterelemente gemäß der
Regeln für diese Unterelemente, hier italic. Wären Attribute im Element
enthalten, würden diese nicht angezeigt, weil ein fehlendes select-Attribut in <apply-templates/> nur Text- und Elementknoten als Kinderknoten auswählt.
Hat das Element <txt> z. B. den Inhalt
<txt>Geben Sie die Lösung als Dateien
<file>A1IhrName.dtd</file> und <file>A2IhrName.xml</file>
ab.</txt>
so würde man im Stylesheet <txt> und <file> wie folgt behandeln:
Kapitel 5 – XSLT und XPath
93
<xsl:template match="file">
<i><xsl:value-of select='.'/></i>
</xsl:template>
<xsl:template match="txt">
<xsl:apply-templates/>
</xsl:template>
Womit dann die Regel für <file> ebenfalls angewandt wird. Die Ausgabe lautet:
<?xml version="1.0" encoding="UTF-8"?>
Geben Sie die Lösung als Dateien
<i>A1IhrName.dtd</i> und <i>A2IhrName.xml</i>
ab.
5.6
Weitere XSLT-Elemente
5.6.1 xsl:element
<xsl:element name='element-name'> ... </xsl:element>
Wird verwendet, um ein Element im Ausgabedokument zu erzeugen. Der
Wert des Attributs name enthält den Namen des zu erzeugenden Elements.
Der Wert des erzeugten Elements ist der Inhalt von <xsl:element>, also
die Angaben zwischen <xsl:element> und </xsl:element> im Stylesheet. <xsl:element> ist z. B. nützlich, um Elementnamen dynamisch zu
berechnen. Beispiel:
Eingabedokument:
<angabe art='beruf' wert='student'/>
<angabe art='alter' wert='24'/>
Regel im Stylesheet:
<xsl:template match='angabe'>
<xsl:element name='{@art}'>
<xsl:value-of select='@wert'/>
</xsl:element>
</xsl:template>
Ausgabedokument:
94
Einführung in XML
<beruf>student</beruf>
<alter>24</alter>
5.6.2 xsl:attribute
<xsl:attribute name='attr-name'> ... </xsl:attribute>
Dient zur Erzeugung von dynamischen Attributwerten für die erzeugten
Elemente im Ausgabedokument. Dies ist notwendig, wenn der Wert eines
Attributs durch eine Berechnung aus Angaben des Eingabedokuments
ermittelt wird.
Im folgenden Beispiel erzeugen wir aus der Eingabe
<email>
<![CDATA[[email protected]]]>
</email>
im Eingabedokument die Ausgabe:
<a href='mailto:[email protected]'>
<tt>[email protected]</tt>
</a>
Die Regel im Stylesheet lautet:
<xsl:template match='email'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:text>mailto:</xsl:text><xsl:value-of select='.'/>
</xsl:attribute>
<tt><xsl:apply-templates/></tt>
</xsl:element>
</xsl:template>
5.6.3 xsl:text
<xsl:text disable-output-escaping="(yes | no)">
...
</xsl:text>
Normalerweise kann man Text im Stylesheet so angeben, wie er in der
Ausgabe produziert werden soll. Will man aber Leerzeichen, Tabulatoren
etc. beibehalten oder zusätzliche Leerzeichen produzieren, so verwendet
Kapitel 5 – XSLT und XPath
95
man <xsl:text>. Sehr nützlich ist in diesem Zusammenhang das optionale Attribut disable-output-escaping, wodurch man Sonderzeichen
wie < und & produzieren kann. Beispiel:
<xsl:text disable-output-escaping='yes'>
cout << "Ausgabe von Strings in C++";
</xsl:text>
5.6.4 xsl:copy
<xsl:copy> ... </xsl:copy>
Kopiert den aktuellen Knoten (nicht rekursiv) in den Ergebnisbaum. Der
Inhalt von <xsl:copy> besteht aus Anweisungen, die Attribute und ggf.
eine Auswahl der Unterelemente dem erzeugten Element hinzufügen.
kann man verwenden, um Teile eines Dokuments zu
kopieren. Wir erzeugen im folgenden Beispiel aus kunden.xml eine Liste
mit leeren Kundenelementen und deren IDs:
<xsl:copy>
<?xml version='1.0' encoding='ISO-8859-1'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:template match="/">
<kunden><xsl:apply-templates/></kunden>
</xsl:template>
<xsl:template match='/kunden/kunde'>
<xsl:copy><xsl:apply-templates select="@*"/></xsl:copy>
</xsl:template>
<xsl:template match='@*'>
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
Angewandt auf kunden.xml bekommen wir die Ausgabe:
<?xml version="1.0" encoding="UTF-8"?>
<kunden>
<kunde id="0001"/>
<kunde id="0002"/>
<kunde id="0003"/>
<kunde id="1000"/>
</kunden>
96
Einführung in XML
5.6.5 xsl:copy-of
<xsl:copy-of select='expression'/>
Kopiert einen Knoten mit all seinen Unterknoten in den Zielbaum. Dies
ist sehr nützlich, wenn bestimmte Teile aus dem Quelldokument in das
Zieldokument unverändert übertragen werden sollen.
Hat man in einer Sprache für die Erstellung von Manuskripten z. B.
das Element <table> für die Erstellung von Tabellen aus HTML übernommen, so braucht man in einem Stylesheet zur Umwandlung in HTML
lediglich die Tabelle zu kopieren. Somit lautet die Regel für <table>:
<xsl:template match="table">
<br/>
<xsl:copy-of select="."/>
<br/>
</xsl:template>
5.6.6 xsl:comment
Kommentare im Ausgabedokument können mit <xsl:comment> erzeugt
werden. Beispiel:
<xsl:comment>
Visualisierung in SVG, Datum 23.11.2012
</xsl:comment>
produziert den Kommentar im Ausgabedokument:
<!--Visualisierung in SVG, Datum 23.11.2012-->
5.6.7 xsl:processing-instruction
<xsl:processing-instruction name='target'>
...
</xsl:processing-instruction>
Steueranweisungen können mit XSLT ebenfalls erzeugt werden. Dabei
können Attribute für die erzeugte Steueranweisung durch einfachen Text
im Stylesheet produziert werden (nicht mit <xsl:attribute>). Beispiel:
Quelldokument:
Kapitel 5 – XSLT und XPath
97
<db product='MySQL'>
...
</db>
Im Stylesheet:
<xsl:processing-instruction name='db-binding'>
dbms="<xsl:value-of select='/db/@product'/>"
</xsl:processing-instruction>
Dies produziert die PI:
<?db-binding dbms="MySQL"?>
5.7
XSLT-Kontrollstrukturen
Neben der rekursiven Ausführung von Regeln, existieren in XSLT mehrere Elemente für Kontrollstrukturen. Diese bekommen meistens einen
oder mehrere XPath-Ausdrücke in einem Attributwert (z. B. test oder
select) übergeben.
5.7.1 xsl:if
<xsl:if test='boolean-expression'> ... </xsl:if>
Führt den Inhalt zwischen Start- und Endtag aus, wenn der Testwert wahr
ist. Im folgenden Beispiel wird hinter jedem Namen, außer dem letzten,
ein Komma angehängt:
<xsl:template match="namelist/name">
<xsl:apply-templates/>
<xsl:if test="not(position()=last())">, </xsl:if>
</xsl:template>
Im folgenden wird in der erzeugten Tabelle jede zweite Zeile gelb gefärbt:
<xsl:template match="item">
<tr>
<xsl:if test="position() mod 2 = 0">
<xsl:attribute name="bgcolor">yellow</xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</tr>
</xsl:template>
98
Einführung in XML
5.7.2 xsl:for-each
<xsl:for-each select='node-set-expression'>
...
</xsl:for-each>
Diese Schleifenkonstruktion wendet die im Inhalt angegebenen Formatieranweisungen nacheinander auf alle durch select ausgewählten Knoten an. Obwohl die Schleife meist mit Templates ersetzbar wäre, ist es oft
leichter, nach einer Auswahl des gesuchten Vaters mit <xsl:template>
dessen Söhne gezielt mit <xsl:for-each> zu verarbeiten. <xsl:foreach> kann ein <xsl:sort>-Element enthalten, um die Knoten zu sortieren.
Im folgenden Beispiel erzeugen wir innerhalb der Regel für einen
Kunden eine Liste aller Bücher auf seiner Wunschliste:
<xsl:template match='kunden/kunde'>
<xsl:text>Bücherwünsche von: </xsl:text>
<xsl:value-of select="name"/>
<ol>
<xsl:for-each select="wunschliste/buecher/buch">
<li>
<xsl:value-of select="titel"/>
</li>
</xsl:for-each>
</ol>
</xsl:template>
5.7.3 xsl:choose
<xsl:choose>
<xsl:when test='boolean-expression'> ... </xsl:when>
...
<xsl:otherwise> ... </xsl:otherwise>
</xsl:choose>
Dieses Element wird in aller Regel mehrere Fallunterscheidungen anbieten, die jeweils mit <xsl:when test='...'> eingeleitet werden. Zuletzt
gibt man ein optionales <xsl:otherwise>-Element an, um nichtgefangene Knoten zu verarbeiten.
Kapitel 5 – XSLT und XPath
99
Im folgenden Beispiel erzeugen wir in Abhängigkeit der Wunschliste
eines Kunden eine „Beschreibung“ dieses Kunden:
<xsl:template match='kunden/kunde'>
<p>
<xsl:value-of select="name"/><xsl:text> ist </xsl:text>
<xsl:choose>
<xsl:when test="count(.//cd) > 0 and
count(.//buch) = 0">
<xsl:text>musikalisch.</xsl:text>
</xsl:when>
<xsl:when test="count(.//cd) = 0 and
count(.//buch) > 0">
<xsl:text>lesebegeistert.</xsl:text>
</xsl:when>
<xsl:when test="count(.//cd) > 0 and
count(.//buch) > 0">
<xsl:text>vielseitig interessiert.</xsl:text>
</xsl:when>
<xsl:when test="count(.//cd) = 0 and
count(.//buch) = 0">
<xsl:text>bescheiden.</xsl:text>
</xsl:when>
</xsl:choose>
</p>
</xsl:template>
und bekommen die folgende Ausgabe in HTML:
<p>Müller ist vielseitig interessiert.</p>
<p>Bubart ist lesebegeistert.</p>
<p>Wegner ist musikalisch.</p>
<p>Ahmad ist bescheiden.</p>
5.8
Benannte Templates, Variablen und Parameter
Für wiederkehrende Funktionen kann man ein sog. benanntes Template
erstellen, um dieses an verschiedenen Stellen im Stylesheet zu verwenden. Diese werden wie andere Templates mit <xsl:template> erstellt,
erhalten jedoch ein Attribut name, das den Namen des Templates angibt.
Im folgenden Beispiel erzeugen wir das benannte Template linkLeiste:
100
Einführung in XML
<xsl:template name='linkLeiste'>
...
</xsl:template>
Dieses kann dann an anderen Stellen im Stylesheet wie folgt aufgerufen
werden:
<xsl:call-template name='linkLeiste'>
...
</xsl:call-template>
Sehr wichtig zu erwähnen ist, dass der Kontext innerhalb einer benannten
Vorlage von dem aufrufenden Element kommt.
Innerhalb einer benannten Vorlage können sog. Parameter definiert
werden. Im Beispiel definieren wir den Parameter titelP mit Standardwert Links:
<xsl:template name='linkLeiste'>
<xsl:param name="titelP">Links</xsl:param>
...
</xsl:template>
Der Name eines Parameters wird durch das Attribut name angegeben. Der
Inhalt von <xsl:param> enthält den Standardwert, der verwendet wird,
falls der Aufruf ohne Parameter erfolgt.
Die Übergabe eines Wertes geschieht innerhalb von <xsl:call-temübergeben hier den Wert Software-Links an den Parameter
titelP und überschreiben damit den Standardwert:
plate>.Wir
<xsl:call-template name='linkLeiste'>
<xsl:with-param name="titelP"
select="'Software-Links'"/>
</xsl:call-template>
Alternativ könnte man schreiben:
<xsl:call-template name='linkLeiste'>
<xsl:with-param name="titelP">
Software-Links
</xsl:with-param>
</xsl:call-template>
Kapitel 5 – XSLT und XPath
101
Variablen können überall im Stylesheet definiert werden. Der Name
Variable ist jedoch nicht wörtlich zu nehmen, da es sich in Wirklichkeit
um Konstanten handelt. Im folgenden Beispiel definieren wir die
Variable font mit Wert Times:
<xsl:variable name="font">Times</xsl:variable>
oder
<xsl:variable name="font" select="'Times'"/>
Man achte darauf, dass der Wert eines Parameters oder einer Variable konstant bleibt und nicht neu gesetzt werden kann.
Um den Wert einer Variable oder eines Parameters zu bekommen, verwendet XSLT das $-Zeichen:
<xsl:value-of select='$font'/>
Als Attributwert kann eine Variable oder ein Parameter in geschweiften
Klammern angegeben werden:
<text zeichensatz='{$font}'>Text-Element</text>
5.8.1 Beispiel
Angenommen, wir hätten das folgende Dokument mit XML-Daten über
einen Projektablauf (project.xml):
<?xml version='1.0' encoding="ISO-8859-1"?>
<TASKS nf2type="sett">
<TASK nf2type="ptuplet">
<TASKID>START</TASKID>
<DESCRIPTION>project kickoff</DESCRIPTION>
<DUR>0</DUR>
<ES>0</ES>
<LS>0</LS>
<EF>0</EF>
<LF>0</LF>
<ISOTIME>01-01-1998</ISOTIME>
<REQUIRES nf2type="sett" state="empty"/>
</TASK>
...
<TASK nf2type="ptuplet">
102
Einführung in XML
<TASKID>GDESIGN</TASKID>
<DESCRIPTION>global design</DESCRIPTION>
<DUR>15</DUR>
<ES>21</ES>
<LS>26</LS>
<EF>36</EF>
<LF>41</LF>
<ISOTIME>10-09-1998</ISOTIME>
<REQUIRES nf2type="sett">
<TASK>GSPEC</TASK>
<TASK>STAFF-PREP</TASK>
</REQUIRES>
</TASK>
...
</TASKS>
In einem Stylesheet, das daraus ein GANTT-Diagramm (Projektablaufplan) in SVG produziert, könnte man z. B. benannte Templates wie
drawTask (einen einzelnen Task zeichnen) oder drawCoords (Koordinaten erstellen) schreiben. Wir schauen uns drawCoords an, das nur ein
Koordinatensystem mit fester Größe erstellt (Vereinfachter Ausschnitt des
Stylesheets project-to-svg.xsl):
<?xml version='1.0' encoding="ISO-8859-1"?>
<xsl:stylesheet version='1.0'
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/2000/svg">
<xsl:output method="xml" indent="yes"
media-type="image/svg"/>
...
<xsl:template name="drawCoords">
<xsl:param name="xend">0</xsl:param>
<xsl:param name="yend">0</xsl:param>
<xsl:variable name="CXBegin" select="5"/>
<xsl:variable name="CYBegin" select="5"/>
<line x1="{$CXBegin}" y1="{$CYBegin}"
x2="{$xend}" y2="{$CYBegin}"
style="stroke-width:3;fill:none; stroke:red"/>
<line x1="{$CXBegin}" y1="{$CYBegin}"
x2="{$CXBegin}" y2="{$yend}"
style="stroke-width:3;fill:none; stroke:red"/>
<line x1="{$xend}" y1="{$CYBegin}"
Kapitel 5 – XSLT und XPath
x2="{$xend}" y2="{$yend}"
style="stroke-width:3;fill:none; stroke:red"/>
<line x1="{$CXBegin}" y1="{$yend}"
x2="{$xend}" y2="{$yend}"
style="stroke-width:3;fill:none; stroke:red"/>
...
</xsl:template>
...
<xsl:template match='/'>
<svg>
...
<xsl:call-template name="drawCoords">
<xsl:with-param name="xend" select="600"/>
<xsl:with-param name="yend" select="400"/>
</xsl:call-template>
...
<xsl:for-each select="TASKS/TASK">
... <!-- Hier wird drawTask aufgerufen. -->
</xsl:for-each>
</svg>
</xsl:template>
</xsl:stylesheet>
Das produzierte SVG-Dokument sieht wie folgt aus:
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
<line style="stroke-width:3;fill:none; stroke:red"
y2="5" x2="600" y1="5" x1="5"/>
<line style="stroke-width:3;fill:none; stroke:red"
y2="400" x2="5" y1="5" x1="5"/>
<line style="stroke-width:3;fill:none; stroke:red"
y2="400" x2="600" y1="5" x1="600"/>
<line style="stroke-width:3;fill:none; stroke:red"
y2="400" x2="600" y1="400" x1="5"/>
...
</svg>
103
Einführung in XML
104
Abb. 5–5 Das SVG-Dokument in Chrome
5.9
Ausgabemethode
Mit dem Element <xsl:output> kann die Ausgabemethode festgelegt
werden. Dieses hat die folgende Form (verkürzt):
<xsl:output
method = "xml" | "html" | "text"
encoding = string
omit-xml-declaration = "yes" | "no"
standalone = "yes" | "no"
doctype-public = string
doctype-system = string
indent = "yes" | "no"
media-type = string />
Wichtig sind die folgenden Attribute:
legt die Ausgabemethode fest.
encoding erstellt ein encoding-Attribut im Zieldokument.
indent gibt an, ob der erzeugte Text im Zieldokument eingerückt werden soll.
media-type gibt einen MIME-Typ für das Zieldokument an.
■ method
■
■
■
Kapitel 5 – XSLT und XPath
105
Falls dieses Element fehlt, so versucht der Stylesheet-Prozessor zu entscheiden, welche Ausgabemethode zu verwenden ist. Dabei wird html
verwendet, falls das Rootelement des Zieldokuments <html> ist, sonst gilt
xml als Standardmethode.
Ein Stylesheet-Prozessor verhält sich unterschiedlich je nach Ausgabemethode. Nur bei der Ausgabemethode xml wird Wohlgeformtheit
gefordert. Es muss sich nicht um ein wohlgeformtes vollständiges XMLDokument handeln, sondern nur um eine wohlgeformte externe allgemeine Entität mit gemischtem Inhalt. Das bedeutet, dass man spätestens
dann, wenn man die Ausgabe als Inhalt in ein XML-Wurzelelement setzt,
ein wohlgeformtes vollständiges XML-Dokument erhält.
Die Details für jede Ausgabemethode können dem Standard entnommen werden.
5.10 Ein vollständiges Beispiel
Wir betrachten nochmal das Dokument project.xml. Mit dem Attribut
nf2type beschreiben wir die Struktur eines Elements. So besagt nf2type='sett', dass das Element eine Menge ist. Wir entwickeln ein Stylesheet, das anhand der Angaben in diesem Attribut eine Darstellung des
Dokuments in Form einer geschachtelten HTML-Tabelle erzeugt (project-to-nf2.xsl):
<?xml version="1.0" encoding='ISO-8859-1'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head><title>Projektablauf</title></head>
<body>
<table align="center" border="0">
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
106
Einführung in XML
<!-- Mengen, Listen und Multimengen -->
<xsl:template match="*[@nf2type='sett' or
@nf2type='listt' or @nf2type='msett']">
<td align="left" valign="top">
<table align="center" border="0">
<xsl:apply-templates/>
</table>
</td>
</xsl:template>
<!-- Implizite Tupel -->
<xsl:template match="*[@nf2type='ptuplet']">
<tr bgcolor="#DDDDDD">
<xsl:apply-templates/>
</tr>
</xsl:template>
<!-- Explizite Tupel -->
<xsl:template match="*[@nf2type='gtuplet']">
<xsl:apply-templates/>
</xsl:template>
<!-- Atomare Werte -->
<xsl:template match="*[not(@nf2type)]">
<td><xsl:value-of select="."/></td>
</xsl:template>
<!-- Atomare Werte als direkte Kinder einer Menge -->
<xsl:template
match="*[@nf2type='sett']/*[not(@nf2type)]">
<tr bgcolor="#DDDDDD">
<td><xsl:value-of select="."/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Kapitel 5 – XSLT und XPath
Abb. 5–6 Darstellung mit Chrome
107
109
6 SVG Scalable Vector Graphics
Wir beziehen uns in dieser Übersicht auf das Tutorial von Kevin Lindsey,
das im Web unter http://www.kevlindev.com/tutorials/index.htm
verfügbar ist. Ergänzend lag das Buch
Marcel Salathé: SVG Scalable Vector Graphics für professionelle
Einsteiger, Markt+Technik Verlag, München, 2001
vor.
Lindsey beginnt mit einem SVG-Grundgerüst:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- SVG content goes here -->
</svg>
Darin besagt die Deklaration aus Zeile 1, dass es sich um ein XML-Dokument nach XML 1.0 handelt. Der Aufbau des Dokuments wird durch die
W3C DTD für SVG 1.0 – ein Riesenschema – bestimmt (Zeilen 2 und 3).
Zeile 4 ist das Wurzelelement, das laut DTD ein svg-Element sein
muss. Es definiert auch Namensräume, in Zeile 5 für XLink, um auf
externe Elemente zugreifen zu können, z. B. Bilder.
Zeile 6 ist Platzhalter für den eigentlichen SVG-Code. Zeile 7 schließt
das svg-Element ab.
Einführung in XML
110
6.1
Elementare Graphikelemente
Hier sind einige einfache Formen aus Lindsey.
<circle cx="25" cy="25" r="20" fill="red"/>
<rect x="5" y="5" width="40" height="40"
fill="red"/>
<rect x="5" y="5" rx="5" ry="5" width="40"
height="40" fill="red"/>
<ellipse cx="25" cy="25" rx="20" ry="10"
fill="red"/>
<line x1="5" y1="5" x2="45" y2="45" stroke="red"/>
<polyline points="5,5 45,45 5,45 45,5"
stroke="red" fill="none"/>
Kapitel 6 – SVG - Scalable Vector Graphics
111
<polygon points="5,5 45,45 5,45 45,5"
stroke="red" fill="none"/>
<path d="M5,5 C5,45 45,45 45,5"
stroke="red" fill="none"/>
<defs>
<rect id="rect" width="15" height="15"
fill="red"/>
</defs>
<use x="5" y="5" xlink:href="#rect"/>
<use x="30" y="30" xlink:href="#rect"/>
Einige Kommentare zu den Beispielen, wobei es weniger darauf
ankommt, alle Features und Formen zu besprechen, als ein Gefühl für die
Syntax von SVG zu erlangen.
<circle cx="25" cy="25" r="20" fill="red"/>
Kreise werden durch die Koordinaten des Mittelpunkts und einen Radius
definiert. Fehlen die Koordinaten, wird (0, 0) genommen (linke obere
Ecke des Viewports). Ohne Angaben zu Einheiten wird in Pixel gerechnet. Lindsey schreibt das Füllattribut direkt hinein.
Salathé definiert einen „Stil“ für sein Kreisbeispiel:
<g style="fill:lightgray;stroke:black;stroke-width:5">
<circle cx="120" cy="90" r="80"/>
<circle r="25"/>
</g>
Dies produziert auch eine schwarze Umrandung.
Das Rechteck wird durch eine Höhe, Breite und die Position der linken oberen Ecke definiert:
112
Einführung in XML
<rect x="5" y="5" width="40" height="40" fill="red"/>
<rect x="5" y="5" rx="5" ry="5" width="40" height="40"
fill="red"/>
Die zweite Variante produziert ein Rechteck mit abgerundeten Ecken,
deren Rundungsradien mit rx und ry (analog zur Ellipse) angegeben werden. Wird nur ein Wert genannt, gilt er für rx und ry.
<ellipse cx="25" cy="25" rx="20" ry="10" fill="red"/>
Ellipsen haben einen Mittelpunkt (center) und zwei Radien rx und ry.
Zunächst zeichnen wir eine gerade Linie durch Angabe der Anfangsund Endkoordinaten. Eine Linienbreite wird hier nicht definiert (Default 1
Pixel), ggf. legt man wieder einen Stil style="stroke:red;strokewidth:2" fest.
<line x1="5" y1="5" x2="45" y2="45" stroke="red"/>
<polyline points="5,5 45,45 5,45 45,5" stroke="red"
fill="none"/>
Ein Polygonzug (Salathé: Polylinie) ist eine (meist offene) Folge von
Punkten, die zusammenhängend durch Linien verbunden sind. Jeder
Punkt besteht aus einer x- und y-Koordinate. Daher muss die Liste der
Punkte eine gerade Anzahl von Werten enthalten. Die x- und y-Koordinaten eines Punkts können zwecks besserer Lesbarkeit durch Komma
getrennt werden. Ohne weitere Angabe wird der Zug mit der Farbe
Schwarz gefüllt, das Attribut fill="none" unterdrückt dies.
<polygon points="5,5 45,45 5,45 45,5" stroke="red"
fill="none"/>
Analog ist das Polygon die geschlossene Variante des Zugs mit der impliziten Verbindung des letzten angegebenen Punkts mit dem Startpunkt.
Ohne eigenes Fill-Attribut wird das Polygon schwarz gefüllt.
Formen kann man als Spezialfall eines Pfades betrachten, der im Stil
eines Plotters einen Stift zu einem angegebenen Punkt bewegt (moveto)
und ihn dort absenkt. Mit weiteren Befehlen (lineto, curveto, horizontal
lineto, vertical loneto) zieht der Stift eine Linie oder Kurve.
Kapitel 6 – SVG - Scalable Vector Graphics
113
<path d="M5,5 C5,45 45,45 45,5" stroke="red fill="none"/>
Im Beispiel oben wird eine kubische Bézierkurve gezeichnet. Die Koordinate nach M ist Startpunkt der Linie (moveto), danach kommen die beiden
Kontrollpunkte (bei quadratischen Bézierkurven nur ein Kontrollpunkt),
dann der Endpunkt. Der Buchstabe C ist Abkürzung für curveto, Q wäre
Abkürzung für die quadratische Variante (quadratic curveto).
Die Punktangaben hier waren absolute Werte (bezogen auf den
Ursprung (0, 0). Verwendet man Kleinbuchstaben m, c, q, l, ... dann sind
die folgenden Koordinatenangaben relativ zum vorherigen Punkt.
Erwähnen sollte man auch Bogenkurven als Teile geneigter Ellipsen.
Zuletzt das Beispiel mit den zwei kleinen (identischen) Rechtecken.
<defs>
<rect id="rect" width="15" height="15" fill="red"/>
</defs>
<use x="5" y="5" xlink:href="#rect"/>
<use x="30" y="30" xlink:href="#rect"/>
Der entscheidende Aspekt hier ist die Trennung in Definition des Graphikteils im defs-Element und der Gebrauch mit use-Elementen, die das
zu benutzende Element referenzieren und die aktuellen Koordinaten für
die Plazierung bestimmen.
Genauso können im use-Element Stilangaben fill="..."
gemacht werden.
usw.
Abschließend zu diesem Kurzausblick zu „Formen“ die Erzeugung
eines grünen Kreises (wie erste Form oben) mittels JavaScript und DOMSchnittstelle:
var svgns = "http://www.w3.org/2000/svg";
function makeShape(evt) {
if (window.svgDocument == null)
svgDocument = evt.target.ownerDocument;
var shape = svgDocument.createElementNS(svgns,"circle");
shape.setAttributeNS(null,"cx",25);
shape.setAttributeNS(null,"cy",25);
shape.setAttributeNS(null,"r",20);
shape.setAttributeNS(null,"fill","green");
Einführung in XML
114
svgDocument.documentElement.appendChild(shape);
}
Lindsey stellt auch eine JavaScript Klasse NodeBuilder zur Verfügung,
mit der sich SVG-Objekte erzeugen lassen. Wir gehen hier nicht darauf
ein.
6.2
Elementare Textelemente
Analog zu dem kurzen Abriss oben seien hier einige der Textgestaltungsmöglichkeiten beschrieben.
Vorsicht: Die x-Koordinaten unten sind korrekt, Lindsey gibt auf der
Web-Seite fälschlich x="0" an, hat im SVG-Quelltext aber die richtigen
Werte (wie ich nach einer halben Stunde herausfand – grummel).
<text x="0" y="13" fill="red"
text-anchor="start">Text</text>
<text x="25" y="13" fill="red"
text-anchor="middle">Text</text>
<text x="50" y="13" fill="red"
text-anchor="end">Text</text>
Kapitel 6 – SVG - Scalable Vector Graphics
115
<text x="0" y="13" fill="red">
<tspan>Line 1</tspan>
<tspan x="0" dy="1em">Line 2</tspan>
</text>
<defs>
<path id="textPath"
d="M10,50 C10,0 90,0 90,50"/>
</defs>
<use xlink:href="#textPath" stroke="blue"
fill="none"/>
<text fill="red">
<textPath xlink:href="#textPath">
Text on a Path
</textPath>
</text>
Grundsätzlich werden Texte als text-Elemente beschrieben. Das hat
große Vorteile für die spätere Trennung von Inhalt und Form. SVG akzeptiert grundsätzlich Unicode-Codierungen.
Jeder Text hat eine Startkoordinate. In der Regel ist dies für die xAchse die linke Kante des ersten Buchstabens, für die y-Achse die Grundlinie der Schrift. Die Einschränkung „in der Regel“ betrifft Texte mit
Rotation oder umgekehrter Schriftrichtung. Die Beispiele oben sind im
übrigen korrigiert gegenüber den originalen Seiten im Web (dort immer
x=0).
Text kann eine Bündigkeit haben. Üblich sind „links“, „zentriert“ und
„rechts“. Für SVG wird mit dem Attribut text-anchor und den Werten
start, middle, end bestimmt, wie sich der Text zu einem durch (x, y)-Koordinaten gegebenen Punkt orientiert: bei start (oft kann man auch alternativ left verwenden) bestimmt der Punkt den Start des Textes, bei middle
die Mittelausrichtung um diesen Punkt, bei end den Endpunkt des Textes,
d. h. der Text schließt rechtsbündig an die gedachte x-Linie an.
116
Einführung in XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<svg xmlns="http://www.w3.org/2000/svg">
<text x="0" y="20" fill="red" text-anchor="middle">
Text x=0 middle
</text>
<text x="0" y="40" fill="red" text-anchor="left">
Text x=0 left
</text>
<text x="0" y="60" fill="red" text-anchor="start">
Text x=0 start
</text>
<text x="150" y="80" fill="red" text-anchor="left">
Text x=150 left
</text>
<text x="150" y="100" fill="red" text-anchor="end">
Text x=150 end
</text>
</svg>
Man beachte insbesondere, dass ein y-Wert von 0 den Text verschwinden
lässt. Weitere Beispiele sind der Text oben mit dem Bild unten.
Das vierte Beispiel von Lindsey zeigt die Verwendung von tspan-Elementen, die nur innerhalb eines text-Elements auftreten dürfen. Man
beachte hier auch die relative Positionierung der zweiten Zeile
(dy="..."). tspan-Elemente erben die Eigenschaften der umgebenden
Kapitel 6 – SVG - Scalable Vector Graphics
117
text-Elemente (außer Koordinaten), nicht aber die des vorherigen tspan-
Elements.
Zu beachten ist auch, dass SVG-Text anders als bei HTML nicht
umgebrochen wird. Dies ist Aufgabe einer höheren Anwendungsebene,
etwa eines DTP-Programms, das SVG erzeugt (macht Sinn, ist aber
lästig).
Analog lässt sich ein Text, der mit defs-Elementen definiert wurde,
per tref-Elemente wieder anzeigen, ggf. mit eigenen Stilattributen. Weitere Attribute sind setzbar, etwa die Schriftstärke (Salathé: Schriftdicke horribile dictu): Zahlenwerte 100 bis 900 (nur die ganzen Hunderter) und
die vordefinierten Werte normal, bold, bolder und lighter für das
Attribut font-weight.
6.3
Animation
<rect y="45" width="10" height="10"
fill="red">
<animate attributeName="x" from="0"
to="90" dur="10s"
repeatCount="indefinite"/>
</rect>
<rect x="45" width="10" height="10"
fill="red">
<animate attributeName="y" from="0"
to="90" dur="10s"
repeatCount="indefinite"/>
</rect>
118
Einführung in XML
<rect width="10" height="10" fill="red">
<animate attributeName="x" from="0"
to="90" dur="10s"
repeatCount="indefinite"/>
<animate attributeName="y" from="0"
to="90" dur="10s"
repeatCount="indefinite"/>
</rect>
<rect x="45" width="10" height="10"
fill="red">
<animateTransform
attributeName="transform"
type="rotate" from="0" to="90"
dur="10s"
repeatCount="indefinite"/>
</rect>
<rect x="-5" y="-5" width="10"
height="10" fill="red">
<animateMotion
path="M10,50 C10,0 90,0 90,50"
dur="10s" repeatCount="indefinite"/>
</rect>
<rect x="-5" y="-5" width="10"
height="10" fill="red">
<animateMotion
path="M10,50 C10,0 90,0 90,50"
dur="10s" rotate="auto"
repeatCount="indefinite"/>
</rect>
Unter Animation versteht man (in der Informatik ;-) ) die zeitliche Veränderung von Bildern. Die Animationselemente von SVG beruhen auf den-
Kapitel 6 – SVG - Scalable Vector Graphics
119
jenigen von SMIL (Synchronized Multimedia Integration Language) Animation.
Laut Salathé wurden die folgenden Elemente von SMIL übernommen:
■ <animate>
■ <set>
■ <animateMotion>
■ <animateColor>
Die SVG-spezifischen und SMIL-kompatiblen Elemente und Attribute
sind:
■ <animateTransform>
■ <mpath>
(Attribut des Elements <animateMotion>)
rotate (Attribut des Elements <animateMotion>)
■ path
■
Man sieht an den Beispielen oben schön, wie Attribute von „normalen“
Elementen animiert werden. Im ersten Beispiel etwa die x-Koordinate, im
zweiten Beispiel y, im dritten Beispiel x und y, im vierten Beispiel eine
Rotation um 90 Grad, im fünften und sechsten Beispiel eine Animation
entlang eines Pfades, einmal ohne Rotation des Würfels und einmal mit.
Auf den Web-Seiten von Lindsey kann man dies schön verfolgen, die
Wiederholung ist unbegrenzt (repeatCount="indefinite").
Salathé gibt Beispiele an, bei denen sich etwa der Radius eines Kreises
ändert, etwa innerhalb von 6s von 0 auf 80 wächst.
Damit der Kreis anschließend nicht verschwindet, fügt er ein
fill="freeze" in das <animate>-Element ein. Dieses fill hat nichts
mit dem Füllen von Figuren zu tun. Der Defaultwert für fill ist
"remove" und das Animationsattribut wird auf den Ausgangswert
zurückgesetzt, hier r=0, wodurch der Kreis verschwindet.
120
Einführung in XML
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
width="300" height="200">
<circle cx="120" cy="90" r="0">
<animate attributeName="r"
begin="0s" dur="6s" fill="freeze"
from="0" to="80"/>
</circle>
</svg>
Der abgebildete Schnappschuss wurde ca. 2 bis 3 Sekunden nach dem
Start aufgenommen.
Je Attribut oder Eigenschaft, die zu animieren ist, muss ein <animate>-Element angegeben werden. Dieses kann innerhalb der Objekts,
hier <circle>, definiert sein, oder außerhalb mit xlink:href="#Name".
<circle id="meinKreis" cx="120" cy="90" r="0"/>
<animate xlink:href="#meinKreis" attributeName="r"
begin="0s" dur="6s" fill="freeze"
from="0" to="80"/>
Das animate-Element hat eine große Anzahl von Attributen. Wir präsentieren hier nur die Liste der Attribute (ggf. mögliche Werte in Klammern).
Kapitel 6 – SVG - Scalable Vector Graphics
121
■ xlink:href
■ attributeName
(CSS, XML, auto)
begin (Zeitangaben mit Minuten:Sekunden.Millisek.)
dur (... oder Stunden:Minuten:Sekunden.Millisek.)
end (begin und end können auch mit Events besetzt werden)
repeatCount (Zahl größer 0 oder indefinite)
repeatDur (Zeiteinheit oder indefinite)
■ attributeType
■
■
■
■
■
■ fill
1
■ from
■ to
■ by
■ values
■ calcMode
(discrete, linear, paced, spline)
■ keyTimes
■ keySplines
(replace, sum)
accumulate (none, sum)
■ additive
■
Es folgt eine weitere Reihe von Elementen:
setzen eines Attributs oder einer Eigenschaft während einer
gewissen Zeit auf einen gewissen Wert; Attribute wie bei <animate>.
<animateMotion> Animation eines Objekts entlang eines Pfades;
kann mit Attribute rotate versehen werden, üblicher Wert dann auto.
■ <set>
■
■ <animateColor>
Die folgende Animation („Choleriker à la Wegner“) zeigt eine Kombination der oben genannten Animationseffekte. Die Produktion (inkl. Idee)
benötigte ca. eine Stunde (trial & error).
Sowohl für die Farbe als auch die Variation der quadratischen Bézierkurve wurde mit values="..." gearbeitet, wobei drei Werte eingegeben
wurden, davon Anfangs- und Endwert gleich. Andere Realisierungen sind
sicher möglich.
1. Unglückliche Wortwahl, warum nicht z. B. keep="...".
122
Einführung in XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<circle r="80" cx="150" cy="90"
stroke="black" fill="pink">
<animateColor attributeName="fill"
values="pink;purple;pink"
dur="6s" repeatCount="indefinite"/>
</circle>
<defs>
<circle id="auge" r="5" fill="black"/>
</defs>
<use x="125" y="60" xlink:href="#auge"/>
<use x="175" y="60" xlink:href="#auge"/>
<path d="M120,120 q30,30 60,0"
stroke="black" stroke-width="5" fill="none">
<animate attributeName="d"
values="M120,120 q30,30 60,0;
M120,120 q30,-30 60,0;
M120,120 q30,30 60,0"
dur="6s" repeatCount="indefinite"/>
</path>
</svg>
Kapitel 6 – SVG - Scalable Vector Graphics
6.4
123
Events
6.4.1 Mausereignisse
Lindsey hat eine schöne Liste von Beispielen, die man aber nur in der
Aktion anschauen kann. Es wird jeweils ein Rechteck von rot auf blau
umgefärbt bei Eintritt des Ereignisses.
<rect x="5" y="5" width="40" height="40" fill="red">
<set attributeName="fill" to="blue" begin="click"/>
</rect>
<rect x="5" y="5" width="40" height="40" fill="red">
<set attributeName="fill" to="blue" begin="mousedown"/>
</rect>
<rect x="5" y="5" width="40" height="40" fill="red">
<set attributeName="fill" to="blue" begin="mouseup"/>
</rect>
<rect x="5" y="5" width="40" height="40" fill="red">
<set attributeName="fill" to="blue" begin="mouseover"/>
</rect>
<rect x="5" y="5" width="40" height="40" fill="red">
<set attributeName="fill" to="blue" begin="mouseout"/>
</rect>
<rect x="5" y="5" width="40" height="40" fill="red">
<set attributeName="fill" to="blue" begin="mousemove"/>
</rect>
Das Attribut begin kann sich auch auf das Ereignis eines anderen Objekts
beziehen. Möchte man zum Beispiel erreichen, dass sich das Rechteck
nach dem Klick auf einen Knopf mit der ID button umfärbt, so würde
man begin="button.click" angeben.
6.4.2 Tastaturereignisse
Lindsey gibt Beispiele für Tastaturereignisse, wobei mit jedem Tastendruck z. B. ein Zähler hochgezählt wird. Lindsey verwendet dazu das
Attribut onkeydown für Tastaturereignisse, das zwar vom Adobe SVG-
Einführung in XML
124
Plug-In unterstützt wird, jedoch kein Bestandteil des SVG-Standards ist.
Unten wird eine alternative Lösung gezeigt, die mit der Methode addEventListener() arbeitet.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
onload="init(evt)">
<script type="application/ecmascript">
<![CDATA[
var count = 0;
function init(evt) {
svgDoc = evt.target.ownerDocument;
svgDoc.addEventListener("keypress", press, false);
}
function press(evt) {
svgDoc = evt.target.ownerDocument;
var text = svgDoc.getElementById("text").firstChild;
text.data = ++count;
}
]]>
</script>
<rect width="100%" height="100%" fill="blue"/>
<text id="text" x="25" y="27" fill="yellow">0</text>
</svg>
6.4.3 Dokumentereignisse
Lindsey gibt Beispiele an, wobei ich nur in zwei Fällen Ereignisse auslösen konnte (z. B. bei Resize), wobei dann ein Warnbutton kommt und
anschließend sich das Fenster in der Größe ändert.
■ <svg onabort="notify(evt)" ...>
■ <svg onerror="notify(evt)" ...>
■ <svg onunload="notify(evt)" ...>
■ <svg onscroll="notify(evt)" ...>
■ <svg onzoom="notify(evt)" ...>
■ <svg onresize="notify(evt)" ...>
Kapitel 6 – SVG - Scalable Vector Graphics
125
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
onresize="notify(evt)">
<script type="application/ecmascript">
<![CDATA[
function notify(evt) {
alert("Resize!");
}
]]>
</script>
<rect width="100%" height="100%" fill="blue"/>
</svg>
6.4.4 Animationsereignisse
Hier wird im Beispiel bei Lindsey das rote Rechteck in Bewegung gesetzt
und es erscheint der Text „Running“. Anschließend hält das Rechteck an
und es erscheint der Text „Stopped“. Durch Klicken in das Rechteck wird
die Animation erneut gestartet.
Einführung in XML
126
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg">
<script type="application/ecmascript">
<![CDATA[
function advance(evt, message) {
svgDocument = evt.target.ownerDocument;
var text =
svgDocument.getElementById("text").firstChild;
text.data = message;
}
]]>
</script>
<rect y="20" width="10" height="10" fill="red">
<animate attributeName="x" values="0;40"
begin="click" dur="2s"
onbegin="advance(evt, 'Running')"
onend="advance(evt, 'Stopped')"/>
</rect>
<text id="text" x="5" y="45"
style="text-anchor: start">
Stopped
</text>
</svg>
6.5
Einbindung von Audiodateien
Lindsey enthält noch viele andere nützliche Beispiele, etwa zum Verfolgen der Mauskoordinaten und für Transformationen. Hier nur kurz ein
SVG-Beispiel für das Adobe Plug-In. Bei Anklicken des roten Kreises
ertönt der „Gong“.
<svg xmlns:a="http://www.adobe.com/svg10-extensions"
a:timeline="independent">
<a:audio xlink:href="ding.wav" begin="play.click"/>
<circle id="play" cx="25" cy="25" r="10" fill="red"/>
</svg>
Der SVG-Standard unterstützt im Gegensatz zum Adobe Plug-In keine
Audio- und Videodateien. Die für mobile Geräte vorgesehene Variante
SVG Tiny 1.2 kennt <audio>- und <video>-Elemente. Als Alternative
Kapitel 6 – SVG - Scalable Vector Graphics
127
lassen sich die HTML5-Funktionalitäten zum Abspielen von Audio und
Video nutzen, indem man den SVG-Code in ein HTML-Dokument einbettet.
6.6
Schlussbemerkung
Es gab auch Bestrebungen, SVG als Grundlage für einen Bildschirmmanager zu nehmen („Ximian“). Damit zeichnen sich auch Parallelen zu
anderen graphischen Sprachen und daraus abgeleiteten Widget-Managern
ab, speziell erinnert mich die Entwicklung stark an Tcl/Tk. Im Fall von
SVG ist die Syntax durch XML offener und mit JavaScript hat man eine
weitverbreitete Sprache für die aktiven Elemente.
129
7 XQuery
XQuery ist eine Anfragesprache an XML-Datenbestände, die seit Januar
2007 in der Version 1.0 als W3C-Norm vorliegt [25, 47]. Damit verbunden war eine Überarbeitung von XPath: XPath 2.0 ist jetzt eine syntaktische Untermenge von XQuery 1.0. Warum braucht man eine solche Sprache?
XML-Dokumente sind lesbare Formen der Datendarstellung und
haben große Ähnlichkeit mit hierarchisch strukturierten Tabellen eines
objekt-relationalen DBMS. Dominiert die dokumenten-zentrische Sicht,
dann sind laut [31] die Daten (Dokumente) selten völlig gleich strukturiert, die Reihenfolge der Einträge ist wichtig, es gibt sinngebende Daten
auf allen Ebenen (mixed content), eine Volltextsuche ist unabdingbar,
aber nicht ausreichend. Beispiele sind:
■
■
■
■
■
Zeitschriftenbeiträge, Bücher
Gebrauchsanweisungen, Handbücher
E-Mail
Präsentationen
Verträge
Dominiert die daten-zentrische Sicht, dann sind XML-Dokumente Daten
im herkömmlichen (Datenbank-) Sinn, die Reihenfolge ist oft nicht relevant (Schlüssel!), die Daten sind einheitlich und meist einfach strukturiert, haben Datentypen, sinntragende Daten sind in Blattelementen oder
Attributen, gemischter Inhalt ist selten oder Dekoration. Beispiele sind:
■
Telefonbücher
130
■
■
■
Einführung in XML
wissenschaftliche Daten
Fahrpläne, Flugpläne
Bestellungen
XML unterstützt gut beide Sichten, man denke an Krankenakten mit einfach strukturierten Daten (Geburtsdatum, Adresse, Behandlungstage und
Abrechnungsziffern), mit binären Daten (Röntgenbilder) und wenig
strukturierten Dokumenten (Diagnose, Anamnese, ...). XML bietet sich
daher sowohl für den Datenaustausch als auch für die Datenspeicherung
an.
In letzterem Fall könnte man die Speicherung in einem modifizierten
Datenbanksystem vorsehen, dass (mehr oder minder stark gewandelte)
Dokumente oder Dokumentdaten aufnimmt. Hierzu wird derzeit mit
SQL/XML ein Standard vorbereitet (vgl. den Artikel von Eisenberg und
Melton [32]), der allerdings auch die Abfrage von XML-Dokumenten
mittels der Abfragesprache XQuery, die in SQL eingebettet wird, zulässt.
In diesem Szenario lassen sich Stärken eines DBMS, etwa die Transaktionsverarbeitung und Datensicherungsmechanismen, ausnutzen.
Alternativ kann man sich eine reine XML-Speicherung vorstellen.
Dann arbeitet XQuery direkt auf den Dokumenten, sollte aber wie SQL
deskriptiv formulierte Anfragen ermöglichen. Dies ist ganz ähnlich zur
Arbeitsweise der Templates in XSLT, wenn man die prozeduralen Komponenten (for-each, if-then-else-fi) ignoriert. Wesentlich wird wieder die
Verwendung von XPath sein, um Baumknoten zu adressieren. Das Gegenstück zum Select-from-where (SFW) Paradigma in SQL ist der FLWORAusdruck1 (for, let, where, order-by, return), der auch die Neustrukturierung des Ausgaberesultats regelt. Dies behandeln wir weiter unten.
Für diese Form der direkten Speicherung sind in den letzten drei Jahren sog. native XML-Datenbanken entwickelt worden (Natix, Tamino,
Xendice), die für manche Anwendungszwecke eine durchaus sinnvolle
Alternative darstellen.
1. Ausgesprochen wie das englische „flower“.
Kapitel 7 – XQuery
131
Die Behandlung von XQuery folgt dem Artikel [29] und dem Buch
[30], beide von Lehner und Schöning, sowie dem übersichtlichen Primer
unter http://www.softwareag.com/xml/tools/xquery_primer.pdf
und der frei verfügbaren Einführung unter http://www.datadirect.com/techzone/xml/xquery/docs/Katz_C01.pdf, bzw. XQuery:
A Guided Tour von Jonathan Robie, verfügbar unter http://www.datadirect.com/developer/xquery/xquerybook/index.ssp . Eine sehr
ausführliche, aber auch etwas langatmige Übersicht bietet das 2006
erschienene Buch von Melton und Buxton [48].
Zur Übung stehen verschiedene XQuery-Implementierungen zur Verfügung, wie z. B. Galax1 oder IPSI-XQ2 der Fraunhofer Gesellschaft.
Abb. 7–1 Ausführung einer XQuery-Abfrage mit IPSI-XQ
1. http://www.galaxquery.org/
2. http://www.ipsi.fraunhofer.de/oasys/projects/ipsi-xq/index_d.html
Einführung in XML
132
7.1
Ausdrücke
7.1.1 Elementare Ausdrücke
Wie in Programmiersprachen unterscheidet man einfache und komplexe
Ausdrücke. Zu letzteren gehören die FLWOR-Ausdrücke, Pfadausdrücke,
arithmetische, konditionale, quantifizierende und Vergleichsausdrücke.
Zu den elementaren Ausdrücken gehören die Variablenreferenzen, wobei
$ einem gültigen XQuery-Bezeichner vorangestellt wird.
$name := "Morad Ahmad"
Rechts steht ein Zeichenkettenliteral, analog lassen sich Zahlen darstellen, die ggf. noch mittels Konstruktoren in Werte eines numerischen Typs
gewandelt werden:
xs:float(1.34e4)
Weitere elementare Ausdrücke sind Funktionsaufrufe, etwa eine Funktion
compare() aus dem Namensraum fn (http://www.w3.org/2005/
xpath-functions).
fn:compare("Patt-Idiotismus", "Patriotismus")
7.1.2 Kommentare
XQuery verwendet die Zeichen (: und :) für potentiell geschachtelte
Kommentare, die ebenfalls elementare Ausdrücke sind.
(: Dies ist ein geschachtelter (: XQuery :) Kommentar :)
XQuery-Kommentare erzeugen keine XML-Kommentare und sind von
diesen wohlunterschieden. Dafür existieren in XQuery Kommentar-Konstruktoren.
7.1.3 Das XQuery-Datenmodell
Das Datenmodell von XQuery (interne Darstellung eines XML-Dokuments) ist identisch mit dem von XPath (siehe Kapitel 5).
Kapitel 7 – XQuery
133
Zentraler Konstrukt ist die Sequenz mit einer beliebigen Anzahl von
Einträgen (atomare Werte oder Knoten), die eine Position haben. Sequenzen sind demnach geordnet, möglich ist die leere Sequenz (). Einelementige Sequenzen und ein einzelnes Element sind gleichwertig. Zur Analyse
und Modifikation gibt es eine Reihe von Funktionen und Operatoren, z. B.
[29]:
■
Kommaoperator
(1, <X/>), (3) liefert
(1, <X/>, 3)
■ to-Operator
2 to 5 liefert (2, 3, 4, 5)
■
Eliminierung von Duplikaten
liefert
(2,5,3,7) wobei die Reihenfolge implementationsabhängig ist.
fn:distinct-values ((2,5,3,7,3,5,5))
XQuery stützt sich auf die in XML Schema vordefinierten Datentypen
(Namensraumpräfix meist xs oder xsd), erweitert diese aber um einige
unspezifische Datentypen. Die Abbildung unten (aus [30]) zeigt die
Typhierarchie von XQuery.
Die allgemeinste Form eines Eintrags in einer XQuery-Sequenz ist
vom Typ item(), die gesamte Sequenz dann vom Typ item()*. Die
Überführung eines XML-Dokuments in eine Instanz des XML-Infoset
und von dort in eine Instanz des XQuery-Modells erfolgt durch die Eingabefunktionen fn:doc() oder fn:collection() und bleibt verborgen.
7.1.4 Einfache Datentypen
XQuery unterstützt drei numerische Datentypen: integer, decimal und
double, welche den Spezifikationen in XML Schema entsprechen. Für
Zahlen gelten die folgenden Regeln:
■
■
■
Jede Zahl kann ein Vorzeichen haben.
Eine Zahl nur aus Ziffern ist vom Typ integer.
Eine Zahl nur aus Ziffern und einem einzelnen Punkt ist vom Typ
decimal.
Einführung in XML
134
item
xs:anyType
xs:anySimpleType
node
xdt:anyAtomicType
attribute
xdt:untypedAtomic
comment
xs:dateTime
element
xs:date
document
xs:time
text
benutzerdefinierte
komplexe Typen
xdt:untypedAny
xs:IDREFS
xs:NMTOKENS
xs:ENTITIES
benutzerdefinierte Listentypen
und Vereinigungstypen
xs:float
processing-instruction
xs:yearMonthDuration
xs:duration
xs:dayTimeDuration
xs:double
xs:integer
xs:string
xs:decimal
xs:normalizedString
xs:gYearMonth
xs:nonPositiveInteger
xs:token
xs:gYear
xs:negativeInteger
xs:gMonthDay
xs:long
xs:gDay
xs:int
xs:gMonth
xs:short
xs:boolean
xs:byte
xs:base64Binary
xs:nonNegativeInteger
xs:language
xs:NMTOKEN
xs:Name
xs:NCName
xs:ID
xs:IDREF
xs:ENTITY
xs:hexBinary
xs:unsignedLong
xs:anyURI
xs:unsignedInt
xs:QName
xs:unsignedShort
xs:NOTATION
xs:unsignedByte
xs:positiveInteger
■
Jede gültige Zahl, die eine e- oder E-Angabe enthält, ist vom Typ
double.
Beispiele:
1
-2
(: An integer :)
(: An integer :)
Kapitel 7 – XQuery
+2
1.23
-1.23
1.2e5
-1.2E5
(:
(:
(:
(:
(:
135
An integer :)
A decimal :)
A decimal :)
A double :)
A double :)
Strings werden wie in XML in einfachen oder doppelten Anführungszeichen angegeben. Beispiel:
"a string"
'a string'
"This is a string, isn't it?"
'This is a "string" '
7.1.5 Lokalisierungspfade
Zur Adressierung von Knoten dienen Lokalisierungspfade. Diese sind die
gleichen wie in XPath. XPath 2.0 [26] verwendet sogar die gleichen Pfadausdrücke wie XQuery (Erweiterungen von XQuery eingebaut).
Lehner und Schöning schreiben [29]: „Ein XPath-Ausdruck besteht
aus einer, durch das /-Zeichen getrennten Folge von Lokalisierungsschritten, die von links nach rechts ausgewertet werden. In jedem Schritt wird
ausgehend von den Ergebnisknoten des vorangehenden Lokalisationsschrittes eine neue Menge von Knoten ermittelt. Initial erfolgt die Auswertung ausgehend von dem so genannten Kontextknoten, der im dynamischen Kontext geführt wird.“
Der initiale Kontext wird bei XQuery durch die oben bereits erwähnten Eingabefunktionen fn:doc() und fn:collection() hergestellt. So
liefert
fn:doc("patienten.xml")//Adresse/Wohnort
eine Sequenz von allen Wohnortknoten (einschließlich Duplikate) aller
registrierten Patienten.
Ein Lokalisierungsschritt besteht wiederum aus drei Komponenten
(Navigationsachse, Knotentest und optional einer Liste von Prädikaten),
die eine gezielte Identifikation von Knoten in einem Dokument ermöglichen, d. h.:
Einführung in XML
136
Achse::Knotentest[Prädikat1]...[PrädikatN]
7.1.6 Navigationsachsen
XQuery unterstützt mit Ausnahme der Namensraumachse alle aus XPath
bekannten Navigationsachsen. Eine Navigationsachse gibt dabei ausgehend von dem aktuellen Knoten den Teil des XML-Dokuments an, in dem
nach Knoten im Rahmen der Auswertung des aktuellen Lokalisierungsschritts gesucht werden soll. Die Abbildung 7–2 unten zeigt visuell die
Suchräume für die unterschiedlichen Achsen.
ancestor-or-self::
ancestor::
preceding::
preceding-sibling::
following::
parent::
self::
ute::
attrib
child::
descendant::
descendant-or-self::
Abb. 7–2 Navigationsachsen in XQuery (nach [29])
following-sibling
Kapitel 7 – XQuery
137
Explizit zu erwähnen ist an dieser Stelle die Achse child::, die alle
direkten Kinder des Kontextknotens liefert und als Standardachse gilt, so
dass die Angabe optional ist. Folgende Ausdrücke sind somit äquivalent:
/child::Patient/child::Name
/Patient/Name
Darüber hinaus sind drei häufig benutzte Abkürzungen zu erwähnen: Die
attribute::-Achse (alle Attribute des Kontextknotens) kann durch das
@-Symbol, die self::-Achse durch einen einfachen und die parent::Achse durch einen zweifachen Punkt (..) ersetzt werden. Somit sind folgende Ausdrücke wieder äquivalent:
/child::Patient/self::*/child::Name/
parent::*/attribute::PatientNr
/Patient/./Name/../@PatientNr
Eine Trennung von zwei Lokalisierungsschritten durch //-Zeichen wird
ebenfalls als Abkürzung geführt und durch einen Ausdruck mit Bezug auf
die descendant-or-self::-Achse während der Auswertung ersetzt. Die
beiden Ausdrücke
Patient//Operation
Patient/descendant-or-self::node()/Operation
Ein /-Zeichen zu Beginn eines XPath-Ausdrucks impliziert, dass in der
Wurzel des Dokuments, in dem sich der aktuelle Kontextknoten befindet,
mit der Auswertung des ersten Lokalisierungsschritts begonnen werden
soll; implizit erfolgt dabei wiederum eine Expansion zu folgendem Ausdruck:
fn:root(self::node())
Ein doppelter Schrägstrich zu Beginn eines Dokuments erweitert den
absoluten Pfadausdruck um die Suche im gesamten Dokument und wird
ersetzt durch folgenden Ausdruck:
fn:root(self::node())/descendant-or-self::node()
Einführung in XML
138
7.1.7 Knotentest und Knotentypen
Der zweite Bestandteil eines XPath-Ausdrucks ermöglicht, die Suche auf
Knoten eines bestimmten Typs einzuschränken. Ein Knotentest kann sich
dabei entweder auf den Namen eines Knotens oder auf den Knotentyp
beziehen. Eine Wildcard (*) wird benutzt, falls kein Knotentest erforderlich ist. Die folgende Tabelle (nach [29]) zeigt einige Lokalisierungsschritte mit Knotentest und den entsprechenden Erklärungen.
child::Name
Kinderknoten mit Bezeichnung
Name
child::*
*
alle Kinderelemente
attribute::*
@*
alle Attribute des aktuellen
Knotens
child::element(Name)
Kinderelemente mit Bezeichnung
Name
child::element(*,
Angestellte_T)
Kindknoten vom Typ Ange-
attribute::attribute(*,
xs:integer)
Attribute mit ganzzahligen Werten
attribute::attribute(Alter)
attribute::Alter
@Alter
Attribut mit Namen Alter
stellte_T
Ein Test bezüglich des Typs eines Knotens bezieht sich wiederum auf
Konstrukte aus dem XQuery-Datenmodell, die in Abb. 7–3 dargestellt
sind. Eine Sequenz ist entweder leer (empty-sequence()) oder vom Typ
item()*. Ein einzelner Eintrag (item()) hat entweder einen atomaren
oder einen Knotentyp (node()). Auf bestimmte Knotenarten kann geprüft
werden. Detaillierter kann dabei auf eine Kombination aus Knotenbe-
Kapitel 7 – XQuery
139
zeichnung und dem dahinter liegenden Typ getestet werden. Der Ausdruck
element(Vorname, xs:string)
liefert alle Elementknoten mit Bezeichnung Vorname mit Inhalt des Knotens vom Datentyp xs:string. Ein Test bezüglich der Knoteneigenschaft
(node()) und damit Ausschluss atomarer Datentypen wird beispielsweise
auch im vorangegangenen Abschnitt bei der Auflösung der Zeichen /
bzw. // verwendet.
document-node()
document-node(element(...))
element(Name)
element()
node()
element(*, Typ)
element(Name, Typ)
comment()
text()
attribute()
attribute(Name)
attribute(Name, Typ)
attribute(*, Typ)
processing-instruction()
processing-instruction(Ziel)
Abb. 7–3 Testmöglichkeiten auf Knotentypen (nach [29])
7.1.8 Prädikate
Eingebettet in ein []-Paar kann jeweils ein Prädikat zur weiteren Filterung von Knoten in einem Lokalisierungsschritt auftreten. So liefert folgender Lokalisierungsschritt nur Chirurgen:
descendant::Arzt[@rolle="Chirurg"]
Einführung in XML
140
Das Prädikat kann entweder ein beliebiger XQuery-Ausdruck sein, dessen
Wahrheitswert durch die Funktion fn:boolean() zur Laufzeit bestimmt
wird, oder die Angabe der gewünschten Kontextposition. Die dritte Operation wird wie folgt bestimmt
child::Operation[3]
Allgemeiner kann ein Positionstest durch Rückgriff auf die Funktion
fn:position() erfolgen, zum Beispiel:
child::Operation[fn:position() = (3 to 5)]
Eine Liste von Prädikaten in einem Lokalisierungsschritt wird schrittweise abgearbeitet. Die beiden folgenden Ausdrücke sind somit nicht
äquivalent:
child::Operation[Transplantation][fn:position() = 3]
child::Operation[fn:position() = 3][Transplantation]
Die Kombination aus Achsen und Tests über Knotentypen bzw. allgemeine Prädikate bildet ein sehr mächtiges Mittel, um gezielt Elemente in
einem XML-Datenbestand lokalisieren zu können.
7.2
FLWOR-Ausdrücke
Neben den oben beschriebenen Pfadausdrücken bilden die FLWOR-Ausdrücke das Zentralstück von XQuery zur Formulierung von Anfragen.
Der Ausdruck bindet Variablen an Ergebnisse von Ausdrücken, wertet
darauf in einer where-Klausel ein Prädikat aus und konstruiert ein neues
Dokument als Resultat.
In dieser Einführung verwenden wir das folgende XML-Dokument
bib.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE bib SYSTEM "bib.dtd">
<bib>
<book year="1994">
<title>TCP/IP Illustrated</title>
<author><last>Stevens</last><first>W.</first></author>
<publisher>Addison-Wesley</publisher>
<price>65.95</price>
</book>
Kapitel 7 – XQuery
141
<book year="1992">
<title>
Advanced Programming in the UNIX Environment
</title>
<author><last>Stevens</last><first>W.</first></author>
<publisher>Addison-Wesley</publisher>
<price>65.95</price>
</book>
<book year="2000">
<title>Data on the Web</title>
<author><last>Abiteboul</last><first>Serge</first></author>
<author><last>Buneman</last><first>Peter</first></author>
<author><last>Suciu</last><first>Dan</first></author>
<publisher>Morgan Kaufmann Publishers</publisher>
<price>55.95</price>
</book>
<book year="1999">
<title>
The Economics of Technology and Content for Digital TV
</title>
<editor>
<last>Gerbarg</last><first>Darcy</first>
<affiliation>CITI</affiliation>
</editor>
<publisher>Kluwer Academic Publishers</publisher>
<price>129.95</price>
</book>
</bib>
Die zugehörige DTD bib.dtd:
<!ELEMENT bib (book*)>
<!ELEMENT book (title, (author+ | editor+),
publisher, price)>
<!ATTLIST book year CDATA #REQUIRED>
<!ELEMENT author (last, first)>
<!ELEMENT editor (last, first, affiliation)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT last (#PCDATA)>
<!ELEMENT first (#PCDATA)>
<!ELEMENT affiliation (#PCDATA)>
<!ELEMENT publisher (#PCDATA)>
<!ELEMENT price (#PCDATA)>
Die folgende Abfrage nach dem Buchtitel der im Jahr 2000 publizierten
Bücher
for $b in fn:doc("bib.xml")//book
142
Einführung in XML
where $b/@year = "2000"
return $b/title
liefert dann das Ergebnis:
<title>Data on the Web</title>
Die Bezeichnung FLWOR (gesprochen „flower“) ist ein Akronym für die
ersten Buchstaben der Klauseln, aus denen ein FLWOR-Ausdruck
besteht. Die Analogie zum select-from-where in SQL ist offensichtlich,
allerdings ist SQL eine Realisierung einer Relationenalgebra, während
XQuery eine Art tupel-relationales Kalkül implementiert. Ein eher trivialer Unterschied liegt darin, dass XQuery auf Groß- und Kleinschreibung achtet.
definiert eine Variable, die eine Liste von Elementen durchläuft
(eine Variable, die einzeln an jedes Element gebunden wird).
let definiert eine Variable, deren Wert eine ganze Liste von Elementen ist (eine Variable, die an die ganze Sequenz gebunden wird).
where filtert ein Ergebnis, so dass nur diejenigen Elemente verbleiben,
die eine Bedingung erfüllen.
order by sortiert das Ergebnis.
return liefert eine Sequenz von Einträgen als Ergebnis des FLWORAusdrucks zurück, die gemäß der angegebenen Elementkonstruktion
erzeugt werden.
■ for
■
■
■
■
Dabei wird ein FLWOR-Ausdruck nur selten genau nach dieser Reihenfolge aufgebaut. Er beginnt mit mindestens einer let- oder for-Klausel,
ggf. mehrere gemischt in beliebiger Reihenfolge, gefolgt von einer optionalen where-Klausel, gefolgt von einer optionalen order by-Klausel,
gefolgt von einer notwendigen return-Klausel.
Die Grundvorstellung ist nun, dass FLWOR-Ausdrücke einen Strom
von Tupeln erzeugen. Jedes Tupel besteht aus einem oder mehreren
(Variable, Wert)-Paaren: Man sagt, ein Wert wird an die Variable gebunden. Dieser Strom durchläuft dann den where-Filter, wird ggf. neu geordnet und erzeugt mittels return eine Ausgabesequenz, die in der Regel
nicht wieder ein vollständiges XML-Dokument ist.
Kapitel 7 – XQuery
143
7.2.1 for- und let-Klausel
Der Unterschied zwischen for und let ist, dass der Wert einer Variable
bei let eine gesamte Liste ist, während bei for die Listenelemente durchlaufen werden. Zunächst ein ganz einfaches Beispiel mit einer for-Klausel.
for $i in (1, 2, 3)
return
<tuple><i>{ $i }</i></tuple>
Ergebnis:
<tuple><i>1</i></tuple>,
<tuple><i>2</i></tuple>,
<tuple><i>3</i></tuple>
In der Regel wird die Variable mittels Pfadausdruck gebunden, so wie im
Buch-Beispiel oben. Gibt es eine Ordnung für die selektierten Werte, an
die die Variable gebunden wird, dann ist auch die Ausgabe so geordnet1.
Durch zwei geschachtelte for-Klauseln kann eine Art Kreuzprodukt
gebildet werden, wie man es vom join in SQL her kennt.
for $i in (1, 2, 3),
$j in (4, 5, 6)
return
<tuple><i>{$i}</i><j>{$j}</j></tuple>
Das Resultat lautet
<tuple><i>1</i><j>4</j></tuple>,
<tuple><i>1</i><j>5</j></tuple>,
<tuple><i>1</i><j>6</j></tuple>,
<tuple><i>2</i><j>4</j></tuple>,
<tuple><i>2</i><j>5</j></tuple>,
<tuple><i>2</i><j>6</j></tuple>,
<tuple><i>3</i><j>4</j></tuple>,
<tuple><i>3</i><j>5</j></tuple>,
<tuple><i>3</i><j>6</j></tuple>
Hinweis: Auch die Schreibweise
1. Die einzig vorstellbare Ausnahme wären Angaben der Art .../@*, also Attributwerte, da Attribute in einem XML-Dokument keine Ordnung haben.
Einführung in XML
144
for $i in (1, 2, 3)
for $j in (4, 5, 6)
return
...
wäre erlaubt. Ebenso einfach eine Abfrage mit einer let-Klausel:
let $i := (1, 2, 3)
return
<tuple><i>{$i}</i></tuple>
Hier wird i an die Sequenz als ganzes gebunden und liefert damit
<tuple><i>1 2 3</i></tuple>
Interessanter ist die Kombination von let-Klausel mit for.
for $i in (1, 2, 3)
let $j := (1, 2, 3)
return
<tuple><i>{ $i }</i><j>{ $j }</j></tuple>
Ergebnis:
<tuple><i>1</i><j>1 2 3</j></tuple>,
<tuple><i>2</i><j>1 2 3</j></tuple>,
<tuple><i>3</i><j>1 2 3</j></tuple>
Der Wert einer „let-Variablen“ wird bei jedem Schleifendurchlauf neu
berechnet, auch wenn man es hier nicht sieht. Das folgende Beispiel
for $i in (1, 2, 3)
let $j := ($i, $i + 1)
return
<tuple><i>{$i}</i><j>{$j}</j></tuple>
zeigt es deutlicher. Das Ergebnis der XQuery ist
<tuple><i>1</i><j>1 2</j></tuple>,
<tuple><i>2</i><j>2 3</j></tuple>,
<tuple><i>3</i><j>3 4</j></tuple>
Ein etwas praktischeres Beispiel, das den Buchtitel und die Anzahl der
Autoren ausgibt, wäre
Kapitel 7 – XQuery
145
for $b in fn:doc("bib.xml")//book
let $c := $b/author
return
<book>{ $b/title, <count>{ count($c) }</count>}</book>
Das Ergebnis lautet:
<book>
<title>TCP/IP Illustrated</title>
<count>1</count>
</book>,
<book>
<title>Advanced Programming in the UNIX
Environment</title>
<count>1</count>
</book>,
<book>
<title>Data on the Web</title>
<count>3</count>
</book>,
<book>
<title>The Economics of Technology and Content for
Digital TV</title>
<count>0</count>
</book>
Formuliert man die Query übrigens wie folgt
for $b in fn:doc("bib.xml")//book
let $c := fn:doc("bib.xml")//author
return
<book>{ $b/title, <count>{fn:count($c)}</count> }</book>
hat jedes der vier Bücher genau fünf Autoren!
Etwas Vorsicht ist geboten mit dem Begriff „Variable“. Wer in der folgenden Abfrage erwartet, dass j von 1 bis 3 hochgezählt wird, sieht sich
getäuscht.
let $j := 0
for $i in ("pi", "pa", "po")
let $j := $j + 1
return
<tuple><i>{$i}</i><j>{$j}</j></tuple>
Das Ergebnis zeigt, dass j in jedem Durchlauf den Wert 1 hat:
146
Einführung in XML
<tuple><i>pi</i><j>1</j></tuple>,
<tuple><i>pa</i><j>1</j></tuple>,
<tuple><i>po</i><j>1</j></tuple>
Das hängt mit den Gültigkeitsbereichen der Variablen zusammen. Der
Standard bestimmt, dass eine in einer for- oder let-Klausel gebundene
Variable in allen nachfolgenden Klauseln sichtbar ist. Somit ist oben die
zum Wert 0 gebundene Variable j sichtbar in der folgenden for-Klausel
und in jeder Iteration mit dem Wert 0 in der rechten Seite der zweiten letKlausel. Dieses j mit Wert 0 wäre auch noch im return gültig, hätte nicht
die zweite let-Klauses für die zu bindende Variable wieder den Bezeichner j gewählt. Diese neue Variable j überdeckt das alte j und wird im Beispiel in allen drei Iterationsschritten an den Wert 1 gebunden und liefert
somit im return dreimal diesen Wert.
Genauso wenig liefert dann
let $sum := 0
for $p in fn:doc("bib.xml")//price
let $sum := $sum + $p
return <gesamt>{$sum}</gesamt>
die Gesamtsumme der Preise im Buchdokument. Vielmehr werden die
vier erzeugten Tupel ausgegeben.
<gesamt>65.95</gesamt>,
<gesamt>65.95</gesamt>,
<gesamt>55.95</gesamt>,
<gesamt>129.95</gesamt>
Das gewünschte Ergebnis lässt sich aber über die sum-Funktion produzieren:
let $p := fn:doc("bib.xml")//price
return <gesamt>{fn:sum($p)}</gesamt>
7.2.2 where-Klausel
Wie bei SQL dient die where-Klausel mit einem Prädikat der Selektion.
Die return-Klausel wird nur für die Tupel evaluiert, die diese Selektion
überlebt haben. In Anlehnung an OQL gibt es die Möglichkeit der Filterung mit Bezug auf Gruppen, ähnlich der HAVING-Klausel in SQL.
Kapitel 7 – XQuery
147
Zunächst aber ein einfaches Beispiel, etwa die Titel der Bücher, die weniger als $60 kosten.
for $b in fn:doc("bib.xml")//book
where $b/price < 60.00
return $b/title
Nur ein Buch erfüllt dieses Kriterium1.
<title>Data on the Web</title>
Geringfügig aufwendiger eine Abfrage nach Büchern mit mindestens
zwei Autoren.
for $b in fn:doc("bib.xml")//book
let $c := $b/author
where fn:count($c) > 1
return $b/title
Die Ausgabe ist der selbe Buchtitel wie oben.
In where-Klauseln kann auch eine existentielle („es existiert ein“) oder
universelle („für alle“) Quantifizierung mittels einer Variable nach some
und every vorgenommern werden. Der gesamte Ausdruck wird dann zu
wahr ausgewertet, falls eine bzw. alle Belegungen der Variablen wahr
ergeben.
for $b in fn:doc("bib.xml")//book
where some $a in $b/author satisfies
($a/last="Stevens" and $a/first="W.")
return $b/title
Hier geht es um Buchtitel mit mindestens einem Autor, der W. Stevens
heißt.
<title>TCP/IP Illustrated</title>,
<title>Advanced Programming in the UNIX Environment</title>
Suchen wir nach Titeln von Büchern, deren Autoren alle W. Stevens heißen, geht das mit
1. wobei wir gegenüber dem Tutorial von Robie nachhelfen mussten. Dort waren alle
Preise über $60, die Abfrage forderte < 50.00, trotzdem erschien der Titel!
148
Einführung in XML
for $b in fn:doc("bib.xml")//book
where every $a in $b/author satisfies
($a/last="Stevens" and $a/first="W.")
return $b/title
und liefert überraschenderweise einen dritten Titel.
<title>TCP/IP Illustrated</title>,
<title>Advanced Programming in the UNIX Environment</title>,
<title>The Economics of Technology and Content for Digital TV</title>
Da dieses Buch keine Autoren hat, erfolgt die Auswertung der whereKlausel mit dem universellen Quantifizierer auf der leeren Folge. Das
ergibt aber immer wahr.
Zuletzt eine Umkehrung der Hierarchie: Titel, die ein Autor geschrieben hat.
<author-list>
{
let $a := fn:doc("bib.xml")//author
for $lname in fn:distinct-values($a/last),
$fname in fn:distinct-values($a[last=$lname]/first)
order by $lname, $fname
return
<author>
<name>{$fname, $lname}</name>
{
for $b in fn:doc("bib.xml")//book
where some $ba in $b/author satisfies
($ba/last=$lname and $ba/first=$fname)
order by $b/title
return $b/title
}
</author>
}
</author-list>
Die Ausgabe lautet
<author-list>
<author>
<name>Serge Abiteboul</name>
<title>Data on the Web</title>
</author>
<author>
<name>Peter Buneman</name>
Kapitel 7 – XQuery
149
<title>Data on the Web</title>
</author>
<author>
<name>W. Stevens</name>
<title>Advanced Programming in the UNIX Environment</title>
<title>TCP/IP Illustrated</title>
</author>
<author>
<name>Dan Suciu</name>
<title>Data on the Web</title>
</author>
</author-list>
7.2.3 order by-Klausel
Durch die order by-Klausel lässt sich die Anordnung der Ergebnisse im
return-Teil beeinflussen. Zunächst einfach die Buchtitel aufsteigend sortiert.
for $t in fn:doc("bib.xml")//title
order by $t
return $t
Die Formatierung des Ergebnisses unten hängt im übrigen von der Formatierung des bib.xml-Dokuments ab, was ggf. nur unschön ist. Gefährlich
ist dagegen ein Leerzeichen nach <title>. Es ist Teil des Titels und
ändert die Sortierfolge, jedenfalls bei galax, auf dem der Test lief!
<title>Advanced Programming in the UNIX Environment</title>,
<title>Data on the Web</title>,
<title>TCP/IP Illustrated</title>,
<title>The Economics of Technology and Content for Digital TV</title>
Durch sog. order modifiers kann die Sortierung beeinflusst werden, z. B.
kann aufsteigende oder absteigende Sortierordnung verlangt werden. Mit
empty greatest bzw. empty least können Einträge, die eine leere
Sequenz bilden, gesteuert werden, im ersten Fall etwa ganz an den
Schluss bei Sortierfolge ascending (aufsteigend). Ferner wird bei stabiler
Multimengensortierung (Angabe stable) die aus dem Quelldokument
stammende relative Ordnung gleicher Elemente beibehalten (vgl. auch
[49]).
150
Einführung in XML
Die folgende Abfrage gibt die Buchtitel sortiert nach dem Nachnamen
des 1. Autors aus. Bei gleichem Nachnamen bestimmt der Vorname die
Reihenfolge. Ist auch dieser gleich (wie in unserem bib.xml Beispiel)
soll die relative Reihenfolge der Bücher erhalten bleiben. Das Buch ohne
Autoren kommt wegen empty least an den Anfang.
for $b in fn:doc("bib.xml")//book
let $a1 := $b/author[1]
stable order by $a1/last empty least, $a1/first empty least
return $b/title
Das Ergebnis lautet:
<title>The Economics of Technology and Content for Digital TV</title>,
<title>Data on the Web</title>,
<title>TCP/IP Illustrated</title>,
<title>Advanced Programming in the UNIX Environment</title>
7.2.4 return-Klausel und Element-Konstruktoren
In der return-Klausel wird das Ergebnis konstruiert, in der Regel als Element-Sequenz in XML-Schreibweise. Das folgende Beispiel ist also eine
gültige XQuery, die ein Dokument mit dem selben Inhalt produziert:
<versuch>Der erste Versuch schlägt nicht immer fehl</versuch>
Darüberhinaus können sog. Konstruktoren verwendet werden. Dabei existiert für jeden der sieben Knotentypen ein Konstruktor, der eine Instanz
von diesem Knotentyp erzeugt. Das obige Element <versuch> kann man
also auch wie folgt erzeugen:
element versuch {
"Der erste Versuch schlägt nicht immer fehl"
}
Üblicherweise spielen aber nur Elemente und Attribute eine Rolle für die
Ausgabeerzeugung. In geschweiften Klammern können beliebige
XQuery-Anweisungen vorkommen. Ebenfalls dürfen diese beliebig
ineinander geschachtelt werden. Auf diese Weise können Knoten (Elemente, Attribute etc.) dynamisch erzeugt werden. Das folgende Beispiel
Kapitel 7 – XQuery
151
<beispiel>
<p>Der Konstruktor:</p>
<p>fn:doc("bib.xml")//book[1]/title</p>
<p>erzeugt:</p>
<p>{fn:doc("bib.xml")//book[1]/title}</p>
</beispiel>
erzeugt die Ausgabe:
<beispiel>
<p>Der Konstruktor:</p>
<p>fn:doc("bib.xml")//book[1]/title</p>
<p>erzeugt:</p>
<p><title>TCP/IP Illustrated</title></p>
</beispiel>
7.3
Die Positionsvariable at
Die for-Klausel in Verbindung mit at erlaubt den Zugriff auf eine Positionsvariable. Im folgenden Beispiel werden die Buchtitel durchnumeriert.
for $t at $i in fn:doc("bib.xml")//title
return <title pos="{$i}">{fn:string($t)}</title>
Ausgabe:
<title pos="1">TCP/IP Illustrated</title>,
<title pos="2">Advanced Programming in the UNIX Environment</title>,
<title pos="3">Data on the Web</title>,
<title pos="4">The Economics of Technology and Content for Digital
TV</title>
Im Tutorial von Robie [50] wird gezeigt, welche Möglichkeiten sich bei
Tabellen ergeben, die als XHTML-Dokumente vorliegen und auf die man
damit per XQuery zugreifen kann.
7.4
Weitere Möglichkeiten von XQuery
XQuery ist nicht nur eine Abfragesprache, sondern eine vollständige
funktionale Sprache. Mit XQuery können also verschiedene Funktionen
zur Verarbeitung von XML-Dokumenten implementiert werden. Wir
erwähnen hier kurz einige dieser Eigenschaften und verweisen für weitere
Details auf den W3C-Standard.
152
Einführung in XML
7.4.1 XQuery Conditional Expressions (If-then-else-Konstrukt)
Für Bücher mit einem Herausgeber erzeuge das Element <herausgeber>,
sonst ein <autor>-Element.
for $b in fn:doc("bib.xml")//book
return
<buch>
{$b/title}
{if (fn:exists($b/editor)) then
<herausgeber>{$b/editor[1]/last}</herausgeber>
else
<autor>{$b/author[1]/last}</autor>
}
</buch>
Ausgabe:
<buch>
<title>TCP/IP Illustrated</title>
<autor><last>Stevens</last></autor>
</buch>,
<buch>
<title>Advanced Programming in the UNIX Environment</title>
<autor><last>Stevens</last></autor>
</buch>,
<buch>
<title>Data on the Web</title>
<autor><last>Abiteboul</last></autor>
</buch>,
<buch>
<title>The Economics of Technology and Content for Digital TV</title>
<herausgeber><last>Gerbarg</last></herausgeber>
</buch>
7.4.2 Eingebaute Funktionen
XQuery hat eine Menge vordefinierter Funktionen und Operatoren, die
zum Teil von anderen Sprachen bekannt sind. Lehner und Schöning [29]
erwähnen fn:count(), fn:exists(), fn:one-or-more() zur Analyse
von Sequenzen und von Knoten mit fn:node-name(), fn:string(),
fn:number(). Das Verfolgen von Referenzen mit fn:id()
und
fn:idref() ist zunächst nur innerhalb eines Dokuments möglich.
Weiterhin existieren aus SQL bekannte Funktionen wie fn:min(),
fn:max() , fn:sum() , das oben bereits erwähnte fn:count() und
Kapitel 7 – XQuery
153
fn:avg().
Die folgende Abfrage liefert z. B. Bücher, die einen höheren
Preis als der Durchschnitt haben:
let $b := fn:doc("bib.xml")//book
let $average := fn:avg($b/price)
return $b[price > $average]
Das Ergebnis lautet:
<book year="1999">
<title>
The Economics of Technology and Content for Digital TV
</title>
<editor>
<last>Gerbarg</last>
<first>Darcy</first>
<affiliation>CITI</affiliation>
</editor>
<publisher>Kluwer Academic Publishers</publisher>
<price>129.95</price>
</book>
Dazu
kommen String-Verarbeitungsfunktionen fn:substring(),
fn:matches() und kalendarische Extraktions- und Konvertierungsfunktionen fn:get-year-from-yearMonthDuration(), fn:adjust-dateTime-to-timezone().
Eine
ausführliche
Beschreibung
dieser
Funktionen
ist
auf
http://www.w3.org/TR/xquery-operators zu finden [27].
7.4.3 Weitere XQuery-Besonderheiten
Wir gehen hier nicht auf weitere Besonderheiten von XQuery ein. Dazu
gehören
• das Modulkonzept zur Einbindung weiterer Bibliotheksmodule,
• das Verarbeitungskonzept mit statischem und dynamischem Kontext,
• die Überführung der Ergebnissequenz in ein XML-Dokument,
• die Vergleichsausdrücke für (komplexe) Knoten und Gruppierungen mit und ohne Duplikatseliminierung (vgl. hierzu auch die
Arbeiten von Wegner et al. [33, 34]).
Einführung in XML
154
7.5
Einschub XQueryX
XQueryX ist eine XML-Sprache für XQuery. Die Spezifikation findet
man auf der Seite http://www.w3.org/TR/xqueryx.
Was kann man sich unter einer XML-Syntax für XQuery vorstellen?
Eine Abfrage, z. B. die folgende (aus XQueryX 1.0: Titel aller Bücher von
Addison-Wesley, die nach 1991 erschienen sind)
<bib>
{
for $b in doc("bib.xml")/bib/book
where $b/publisher = "Addison-Wesley" and $b/@year > 1991
return
<book year="{ $b/@year }">
{ $b/title }
</book>
}
</bib>
lässt sich als ein XML-Dokument hinschreiben, z. B. wie folgt:
<?xml version="1.0"?>
<xqx:module xmlns:xqx="http://www.w3.org/2005/XQueryX"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2005/XQueryX
http://www.w3.org/2005/XQueryX/xqueryx.xsd">
<xqx:mainModule>
<xqx:queryBody>
<xqx:elementConstructor>
<xqx:tagName>bib</xqx:tagName>
<xqx:elementContent>
<xqx:flworExpr>
<xqx:forClause>
<xqx:forClauseItem>
<xqx:typedVariableBinding>
<xqx:varName>b</xqx:varName>
</xqx:typedVariableBinding>
<xqx:forExpr>
<xqx:pathExpr>
<xqx:stepExpr>
<xqx:filterExpr>
<xqx:functionCallExpr>
<xqx:functionName>doc</xqx:functionName>
<xqx:arguments>
<xqx:stringConstantExpr>
<xqx:value>bib.xml</xqx:value>
</xqx:stringConstantExpr>
</xqx:arguments>
</xqx:functionCallExpr>
</xqx:filterExpr>
</xqx:stepExpr>
<xqx:stepExpr>
<xqx:xpathAxis>child</xqx:xpathAxis>
<xqx:nameTest>bib</xqx:nameTest>
</xqx:stepExpr>
Kapitel 7 – XQuery
<xqx:stepExpr>
<xqx:xpathAxis>child</xqx:xpathAxis>
<xqx:nameTest>book</xqx:nameTest>
</xqx:stepExpr>
</xqx:pathExpr>
</xqx:forExpr>
</xqx:forClauseItem>
</xqx:forClause>
<xqx:whereClause>
<xqx:andOp>
<xqx:firstOperand>
<xqx:equalOp>
<xqx:firstOperand>
<xqx:pathExpr>
<xqx:stepExpr>
<xqx:filterExpr>
<xqx:varRef>
<xqx:name>b</xqx:name>
</xqx:varRef>
</xqx:filterExpr>
</xqx:stepExpr>
<xqx:stepExpr>
<xqx:xpathAxis>child</xqx:xpathAxis>
<xqx:nameTest>publisher</xqx:nameTest>
</xqx:stepExpr>
</xqx:pathExpr>
</xqx:firstOperand>
<xqx:secondOperand>
<xqx:stringConstantExpr>
<xqx:value>Addison-Wesley</xqx:value>
</xqx:stringConstantExpr>
</xqx:secondOperand>
</xqx:equalOp>
</xqx:firstOperand>
<xqx:secondOperand>
<xqx:greaterThanOp>
<xqx:firstOperand>
<xqx:pathExpr>
<xqx:stepExpr>
<xqx:filterExpr>
<xqx:varRef>
<xqx:name>b</xqx:name>
</xqx:varRef>
</xqx:filterExpr>
</xqx:stepExpr>
<xqx:stepExpr>
<xqx:xpathAxis>attribute</xqx:xpathAxis>
<xqx:nameTest>year</xqx:nameTest>
</xqx:stepExpr>
</xqx:pathExpr>
</xqx:firstOperand>
<xqx:secondOperand>
<xqx:integerConstantExpr>
<xqx:value>1991</xqx:value>
</xqx:integerConstantExpr>
</xqx:secondOperand>
</xqx:greaterThanOp>
</xqx:secondOperand>
</xqx:andOp>
</xqx:whereClause>
<xqx:returnClause>
<xqx:elementConstructor>
<xqx:tagName>book</xqx:tagName>
<xqx:attributeList>
<xqx:attributeConstructor>
<xqx:attributeName>year</xqx:attributeName>
<xqx:attributeValueExpr>
<xqx:pathExpr>
155
156
Einführung in XML
<xqx:stepExpr>
<xqx:filterExpr>
<xqx:varRef>
<xqx:name>b</xqx:name>
</xqx:varRef>
</xqx:filterExpr>
</xqx:stepExpr>
<xqx:stepExpr>
<xqx:xpathAxis>attribute</xqx:xpathAxis>
<xqx:nameTest>year</xqx:nameTest>
</xqx:stepExpr>
</xqx:pathExpr>
</xqx:attributeValueExpr>
</xqx:attributeConstructor>
</xqx:attributeList>
<xqx:elementContent>
<xqx:pathExpr>
<xqx:stepExpr>
<xqx:filterExpr>
<xqx:varRef>
<xqx:name>b</xqx:name>
</xqx:varRef>
</xqx:filterExpr>
</xqx:stepExpr>
<xqx:stepExpr>
<xqx:xpathAxis>child</xqx:xpathAxis>
<xqx:nameTest>title</xqx:nameTest>
</xqx:stepExpr>
</xqx:pathExpr>
</xqx:elementContent>
</xqx:elementConstructor>
</xqx:returnClause>
</xqx:flworExpr>
</xqx:elementContent>
</xqx:elementConstructor>
</xqx:queryBody>
</xqx:mainModule>
</xqx:module>
Diese Form einer Query als XML-Dokument ist recht unlesbar und hat
keinen so großen praktischen Nutzen. Man kann aber so zeigen, dass das
Modell abgeschlossen ist, man kann Queries auf Queries starten, gewisse
Syntaxanalysen mit dem Parser erledigen und man kann Queries einheitlich abspeichern. Dafür gibt es ein XML Schema, das alle formulierbaren
Queries abdeckt. Das XML Schema zu XQueryX findet sich unter
http://www.w3.org/TR/xqueryx#Schema.
157
8 SQL/XML
8.1
XML und Datenbanken
8.1.1 Was ist SQL/XML?
Die zunehmende Verbreitung von XML als Datenrepräsentations- und
Datenaustauschsprache bringt das offensichtliche Problem mit sich, wie
die dokumentenorientierte Sicht von XML mit der tabellenorientierten
Sicht der Datenbankwelt in Einklang gebracht werden kann. Errungenschaften der heute ausgereiften relationalen DBMS-Technik sind:
■
■
■
■
■
■
Transaktionsverarbeitung
Hohes Datenvolumen
Integrität, Konsistenz durch Redundanzfreiheit (Normalisierung)
Recovery, Backup
Indexunterstützung, Aggregationen, Data Mining
Einheitliche Schnittstelle durch SQL (Structured Query Language)
Mit SQL/XML gibt es nun einen Ansatz, beide Welten miteinander zu verbinden. SQL/XML ist eine ISO-Norm [37], die als Anhang 14 zu
SQL:2003 zunächst als First Edition veröffentlicht wurde und die Ende
2005 in einer zweiten Auflage (Second Edition) zur Abstimmung gestellt
wurde (Final Committee Draft, FCD). In 2008 und 2011 gab es Überarbeitungen der Norm. Wir zitieren hier im Wesentlichen die Arbeiten von
Eisenberg und Melton [32], die Übersicht von Funderburk, Malaika und
Reinwald [35], die noch auf SQL/XML:2003 beruht, sowie das Buch von
Can Türker[36]. Eine weitere gute Quelle sind die Seiten der sqlx.org.
Einführung in XML
158
Die Aufgabe von SQL/XML besteht aus einem Austausch in zwei Richtungen. Die eine Richtung, hier als „publish“ bezeichnet, baut aus Tabel-
„publish“
XQuery
„extract/store“
SQL
DBMS-Welt
Dokumenten-Welt
lendaten XML-Dokumente auf. Hierfür sind Publikationsfunktionen
nötig, die Aufgabe ist sehr ähnlich der Erzeugung von dynamischen WebSeiten aus Datenbanken, etwa mit MySQL und PHP.
Zugleich kann man die Umformung von SQL-Daten in XML-Werte
(values) als Sichtgenerierung im relationalen Modell betrachten, wobei
eine Speicherung in XML-Dokumenten und spätere Weiterverarbeitung
eine sog. materialisierte Sicht (materialized view) wäre, was nicht die
Regel in SQL ist. In SQL wird eine Abfrage auf einer Sicht (die ja wiederum auch auf einer Abfrage beruht) zu einer einzigen Abfrage auf den
Basistabellen zusammengefasst und dann ausgewertet, so dass nie Resultate aus potentiell veralteten Daten geliefert werden. Die Publish-Funktion als Sichtgenerierung macht es ferner möglich, Strukturen zu erzeugen, die eigentlich im relationalen Modell nicht vorgesehen sind, z.B.
geordnete Listen, Baumstrukturen, usw.
Für die Gegenrichtung gibt es zwei Ansätze. Zum Einen kann man
einen Datentyp XML-Type für „XML-wertige“ Spalten definieren. Eine
solche Spalte nimmt komplette Dokumente auf. In der IBM DB2 Implementierung (DB2 XML Extender) heißt diese Speicherform XML
Column und ist gedacht für „intakte“ Dokumente. Damit wird zum Ausdruck gebracht, dass es aus rechtlichen Gründen oft notwendig ist, XMLDaten (Bestellungen, E-Mails, Zusagen, Abwicklungsdaten, usw.) unverändert als Ganzes abzuspeichern.
Kapitel 8 – SQL/XML
159
Trotzdem wird für diese Form eine Indexunterstützung angeboten
(sog. side tables), die sich als Pfadausdruck („mit welchen Werten soll der
Index arbeiten, wonach wird indexiert“) in einer sogenanten DAD angeben lassen. DAD steht für Document Access Definition, selbst auch ein
XML-Dokument, das zunächst angibt, wie man von relationalen Tabellen
zu XML kommt (also eher die publish-Seite), aber eben auch für Indexfestlegungen und die Gegenrichtung (relationale Datengewinnung aus
XML-Dokumenten) gebraucht wird. Die DAD-Pfadausdrücke sind defacto eingeschränkte XPath-Ausdrücke. Die Norm selbst kennt allerdings
keine DADs.
Zugleich lässt sich aus SQL heraus über eine stored procedure Schnittstelle auf Inhalte, etwa einen einzelnen Elementtext, solcher Dokumente
zugreifen, die als Ganzes in einer Spalte abgespeichert sind. Die Schnittstelle ist praktisch der XQuery-Prozessor. In DB2 erfolgt die physische
Speicherung entweder als XMLVarchar für bis zu 3K Größe in der Datenbank selbst, als XMLCLOB (XML character large object) für bis zu 32K
auch in der Datenbank, und als XMLFILE im lokalen Dateisystem.
Gespeicherte Dokumente lassen sich beim Einfügen in der Spalte und
auch nachträglich validieren. Hierzu führt die Norm für den Spaltentyp
XML Variationen ein, speziell auch für den Fall, dass der gespeicherte
Wert kein vollständiges, valides Dokument ist sondern z.B. nur eine
Sequenz von unterschiedlichen Werten.
Die zweite, etwas spannendere Speicherform ist XML Collection Storage (DB2 Sprechweise), in SQL/XML als XMLTable Pseudofunktion
bekannt. Hier geht es um das „Zerkleinern“ (shredding) der XML-Strukturen, um die Datenelemente in den „flachen“ (1. Normalform!) Tabellen
des DBMS abzulegen.
Der Prozess ist analog zur Normalisierung, d.h. eine hierarchische 1:nBeziehung (z.B. BESTELLUNGEN:BESTELLPOSTEN - Bestellungen
bestehen aus mindestens einem, meist mehreren Bestellposten, ein
Bestellposten gehört zu genau einer Bestellung), wird in zwei Tabellen
abgelegt und über Fremdschlüsselattribute verknüpft (jeder Bestellposten
hat eine Spalte Bestell-Id, der dort eingetragene Wert ist der „fremde“
Schlüssel der Bestellung aus der BESTELLUNGEN-Tabelle).
Einführung in XML
160
Die Schwierigkeit ist allerdings, dass XML-Dokumente nicht so strikt
aufgebaut sind. So werden Beziehungen mit Schlüsseleigenschaft meist
über Attribute vom Typ ID und Verweise darauf mit IDREF abgebildet,
ein Element könnte aber auch mit seinem Textinhalt als Schlüssel dienen,
was dann in der Schemadefinition als solches zu markieren wäre und bei
UPDATE- oder INSERT-Operationen auf dem Dokument geprüft werden
müsste. Die Arbeiten in XML hierzu stehen erst noch am Anfang.
Ein „geschreddertes“ Dokument kann als virtuelle Tabelle in eine
bereits existierende SQL-Basistabelle eingefügt werden oder an einem
regulären SQL-Ausdruck, etwa auch einem Join, teilnehmen. Details folgen weiter unten.
8.2
XML-Publikationsfunktionen
8.2.1 Anwendungsbeispiel
Als Anwendungsbeispiel verwenden wir vier Tabellen aus [35] mit
Bestellungen, Bestellposten, Produkten und Kunden:
ORDER_ID
CUSTOMER_ID
SHIP_METHOD
date
STATUS
100
777
UPS
23.10.99
shipped
101
777
USPS
25.01.02
accepted
102
888
UPS
05.02.02
shipped
Tab. 8–1 Tabelle „Order“
ORDER_ID
ITEM_NUMBER
PRODUCT_ID
QUANTITY
PRICE
100
1
1001
1
108,25
100
2
1002
2
17,42
101
1
1002
5
17,42
102
1
1003
1
104,10
Tab. 8–2 Tabelle „Order_Items“
Kapitel 8 – SQL/XML
161
PRODUCT_ID
DESCRIPTION
STOCK
PRICE
1001
Sound Blaster Audigy MP3+
1
108,25
1002
Travel Alarm With Radio
1
17,42
1003
5.1 Surround Sound Speaker System
1
104,10
Tab. 8–3 Tabelle „Product“
CUSTOMER_ID
NAME
ADDRESS
777
William P Barnes
104 West Avery Lane, CA
888
Shirley Jackson
1344 Pennsylvania Ave, OH
Tab. 8–4 Tabelle „Customer“
8.2.2 Default View
Die zunächst recht elementare Einsicht ist, dass jede relationale Tabelle
(damit auch jedes SQL-Abfrageergebnis) wie folgt in ein XML-Dokument gewandelt werden kann.
<table-name>
<row>
<column1-name>data</column1-name>
<column2-name>data</column2-name>
<column3-name>data</column3-name>
...
</row>
<row>
<column1-name>data</column1-name>
<column2-name>data</column2-name>
<column3-name>data</column3-name>
...
</row>
...
</table-name>
162
Einführung in XML
Diese Sicht wird deshalb default view genannt. Sie könnte mit XSLT
umgeformt werden. Für die Tabelle Customer oben liefert der folgende
Oracle-spezifische Aufruf
SELECT DBMS_XMLGEN.getXML('SELECT * FROM "Customer"')
FROM DUAL;
das folgende Dokument:
<ROWSET>
<ROW>
<CUSTOMER_ID>777</CUSTOMER_ID>
<NAME>William P Barnes</NAME>
<ADDRESS>104 West Avery Lane, CA</ADDRESS>
</ROW>
<ROW>
<CUSTOMER_ID>888</CUSTOMER_ID>
<NAME>Shirley Jackson</NAME>
<ADDRESS>1344 Pennsylvania Ave, OH</ADDRESS>
</ROW>
</ROWSET>
Eine etwas gefälligere Umformung ließe sich mit der folgenden in eine
SQL-Abfrage eingebetteten XQuery über der default view bewerkstelligen (ebenfalls für Oracle):
SELECT XMLQUERY('
<customerList>{
for $c in ora:view("UEBUSER104","""Customer""")/ROW
return <customer id="{ $c/CUSTOMER_ID }">
<name>{ fn:string($c/NAME) }</name>
<address>{ fn:string($c/ADDRESS) }</address>
</customer>
}</customerList>' RETURNING CONTENT NULL ON EMPTY)
FROM DUAL;
Das Ergebnis lautet:
<customerList>
<customer id="777">
<name>William P Barnes</name>
<address>104 West Avery Lane, CA</address>
</customer>
<customer id="888">
<name>Shirley Jackson</name>
Kapitel 8 – SQL/XML
163
<address>1344 Pennsylvania Ave, OH</address>
</customer>
</customerList>
8.2.3 Funktionen zur XML-Erzeugung
Hier geht es nun um spezielle SQL-Erweiterungen, die sog. publishing
functions, auch als SQLX-Funktionen bekannt:
■
■
■
■
■
■
XMLElement und XMLAttributes konstruieren XML-Elemente mit
Attributen
XMLForest konstruiert eine Sequenz von XML-Elementen
XMLConcat verkettet XML-Elemente
XMLAgg aggregiert XML-Elemente
XMLComment zur Erzeugung von Kommentaren
XMLPI zur Erzeugung von Prozessorinstruktionen
Die folgenden Abfragen wurden unter Oracle 11 getestet, wobei die Beispielabfragen aus [35] z.T. einige Fehler enthielten.
Abfrage 1:
SELECT XMLELEMENT(
NAME "order",
XMLATTRIBUTES(o.ORDER_ID),
XMLELEMENT(NAME "signdate", o."date"),
XMLELEMENT(
NAME "order_value",
(SELECT SUM(QUANTITY * PRICE)
FROM "Order_Items" oi
WHERE oi.ORDER_ID = o.ORDER_ID)
)
)
FROM "Order" o
WHERE STATUS = 'shipped';
Das Ergebnis sind zwei Zeilen, von denen jede ein XML-Element
<order> enthält:
<order ORDER_ID="100">
<signdate>23.10.99</signdate>
164
Einführung in XML
<order_value>143,09</order_value>
</order>
<order ORDER_ID="102">
<signdate>05.02.02</signdate>
<order_value>104,10</order_value>
</order>
Hinweis: Der Standard erlaubt in XMLAttributes die Form
xml-attribute-value [ AS xml-attribute-name ]
d.h., man hätte oben XMLATTRIBUTES(o.ORDER_ID AS "id") schreiben
können und dann den üblichen Attributnamen id eingetragen. Die Anführungszeichen um id müssen gesetzt werden, damit der Attributname nicht
komplett in Großbuchstaben ausgegeben wird. In Abfrage 4 unten wird
diese Form gewählt.
Abfrage 2 verwendet XMLForest. Die Funktion ist eine Abkürzungsmethode zur Erzeugung einer Folge von Elementen aus einer variablen Liste
von SQL-Werten, die sich ggf. auch zur Laufzeit aus Ausdrücken über
Spaltenwerten errechnen. In diesem Fall muss ein Alias-Name für das zu
generierte Element genannt werden (AS ...), sonst kann darauf verzichtet werden und XMLForest nimmt dann den Spaltennamen wie im default
view:
SELECT XMLELEMENT(
NAME "order",
XMLFOREST(
ORDER_ID AS "ID",
CUSTOMER_ID AS "customerID",
SHIP_METHOD AS "shipMethod"
)
)
FROM "Order"
WHERE STATUS = 'shipped';
Ergebnis 2:
<order>
<ID>100</ID>
<customerID>777</customerID>
<shipMethod>UPS</shipMethod>
</order>
Kapitel 8 – SQL/XML
165
<order>
<ID>102</ID>
<customerID>888</customerID>
<shipMethod>UPS</shipMethod>
</order>
Abfrage 3 verwendet XMLConcat. Die Funktion nimmt eine variable
Anzahl von XML-Werten als Ausdrücke und erzeugt einen einzelnen
XML-Wert als Folge von XML-Werten:
SELECT
XMLELEMENT(
NAME "order",
XMLCONCAT(
XMLELEMENT(NAME "ID", ORDER_ID),
XMLELEMENT(NAME "customerID", CUSTOMER_ID),
XMLELEMENT(NAME "shipMethod", SHIP_METHOD)
)
)
FROM "Order"
WHERE STATUS = 'shipped';
Das Ergebnis 3 ist gleich dem vorherigen Ergebnis bei Abfrage 2.
Abfrage 4 verwendet XMLAgg, die Aggregationsfunktion. Mit ihr lassen
sich 1:n-Beziehungen in XML abbilden. Man beachte deshalb den Join
unten, mit dem Bestellungen (Order) und Bestellposten (Order_Items)
verknüpft werden:
SELECT XMLELEMENT(
NAME "order",
XMLATTRIBUTES(o.ORDER_ID AS "ID"),
XMLAGG(
XMLELEMENT(
NAME "item",
XMLATTRIBUTES(oi.ITEM_NUMBER AS "ItemNumber"),
XMLFOREST(oi.PRODUCT_ID AS "ProductID",
oi.QUANTITY AS "Quantity")
)
ORDER BY oi.ITEM_NUMBER
)
)
FROM "Order" o, "Order_Items" oi
WHERE o.ORDER_ID = oi.ORDER_ID
GROUP BY o.ORDER_ID;
166
Einführung in XML
Ergebnis 4:
<order ID="100">
<item ItemNumber="1">
<ProductID>1001</ProductID>
<Quantity>1</Quantity>
</item>
<item ItemNumber="2">
<ProductID>1002</ProductID>
<Quantity>2</Quantity>
</item>
</order>
<order ID="101">
<item ItemNumber="1">
<ProductID>1002</ProductID>
<Quantity>5</Quantity>
</item>
</order>
<order ID="102">
<item ItemNumber="1">
<ProductID>1003</ProductID>
<Quantity>1</Quantity>
</item>
</order>
Die letzte Abfrage 5 ist aufgebaut wie Abfrage 4, nur erweitert um Kundenname und Produktbeschreibung, damit auch diese Tabellen mal verwendet werden. Abfrage 5 ist nicht in [35] enthalten.
SELECT XMLELEMENT(NAME "order",
XMLATTRIBUTES(o.ORDER_ID AS "ID",
c.NAME AS "CustomerName"),
XMLAGG(
XMLELEMENT(
NAME "item",
XMLATTRIBUTES(oi.ITEM_NUMBER AS "ItemNumber"),
XMLFOREST(
oi.PRODUCT_ID AS "ProductID",
p.DESCRIPTION AS "ProductDescription",
oi.QUANTITY AS "Quantity"
)
)
ORDER BY oi.ITEM_NUMBER
)
)
Kapitel 8 – SQL/XML
167
FROM "Order" o, "Order_Items" oi, "Customer" c, "Product" p
WHERE o.ORDER_ID = oi.ORDER_ID AND
c.CUSTOMER_ID = o.CUSTOMER_ID AND
p.PRODUCT_ID = oi.PRODUCT_ID
GROUP BY o.ORDER_ID, c.NAME;
Ergebnis 5:
<order ID="100" CustomerName="William P Barnes">
<item ItemNumber="1">
<ProductID>1001</ProductID>
<ProductDescription>
Sound Blaster Audigy MP3+
</ProductDescription>
<Quantity>1</Quantity>
</item>
<item ItemNumber="2">
<ProductID>1002</ProductID>
<ProductDescription>
Travel Alarm With Radio
</ProductDescription>
<Quantity>2</Quantity>
</item>
</order>
<order ID="101" CustomerName="William P Barnes">
<item ItemNumber="1">
<ProductID>1002</ProductID>
<ProductDescription>
Travel Alarm With Radio
</ProductDescription>
<Quantity>5</Quantity>
</item>
</order>
<order ID="102" CustomerName="Shirley Jackson">
<item ItemNumber="1">
<ProductID>1003</ProductID>
<ProductDescription>
5.1 Surround Sound Speaker System
</ProductDescription>
<Quantity>1</Quantity>
</item>
</order>
168
Einführung in XML
8.2.4 Document Access Definitions in DB2 Extender
In [35] folgt dann ein Abschnitt über das Publizieren von XML-Dokumenten aus relationalen Tabellen mittels einer sog. Document Access
Definition (DAD) als Teil von IBMs DB2 XML Extender. DADs sind
selbst wieder XML-Dokumente. DADs bestehen aus einem SQL
SELECT, das die Datenanlieferung regelt und einer an XML Schema
erinnernden Strukturangabe, die regelt, welche Werte zu Attributen werden und welche zu Elementen, sowie Angaben, wie die Elemente zu
schachteln sind.
Die ISO-Norm enthält keine DADs, deswegen übergehen wir diesen
Teil. Klar ist, dass die automatische Erzeugung von XML-Dokumenten
nach einer XML-Schema-Angabe eine sehr attraktive Möglichkeit ist.
8.3
Die Funktionen XMLQuery und XMLCast
Mit der Funktion XMLQuery [32] kann das Ergebnis einer XQueryAbfrage an SQL weitergereicht werden. Eine Einsatzmöglichkeit ist die
Suche mit Werten aus den Tabellen nach XML-Werten in einem XMLDokument, zum Beispiel für einen Join oder um daraus wieder ein XMLDokument zu erzeugen. Die allgemeine Syntax lautet:
XMLQUERY ( xquery-expression
[ PASSING { BY REF | BY VALUE } argument-list ]
RETURNING { CONTENT | SEQUENCE } [ BY REF | BY VALUE ]
{ NULL ON EMPTY | EMPTY ON EMPTY } )
Die xquery-expression ist der eigentliche XQuery-Ausdruck, der als
Literal angegeben werden muss (also mit einfachen Anführungszeichen).
Die PASSING-Klausel enthält eine durch Kommas getrennte Liste von
Argumenten. Jeder Eintrag der Liste hat die Syntax
value-expression [ AS identifier ] [ BY REF | BY VALUE ]
und bindet den Wert eines SQL-Ausdrucks an eine globale XQueryVariable in dem XQuery-Ausdruck. Eines der Argumente darf ohne den
Zusatz AS identifier stehen und definiert den Kontextknoten für die
XQuery-Abfrage. Für die gesamte Liste oder für jedes einzelne Argument
Kapitel 8 – SQL/XML
169
kann bestimmt werden, ob die Übergabe als kopierter Wert oder per Referenz erfolgt.
Im Beispiel aus [32] wird das Stringliteral 'A.Eisenberg' an var1
gebunden. Das zweite Argument buying_agents wird an den aktuellen
Kontext des XQuery-Ausdrucks gebunden, weil keine Variable angegeben ist (AS ... fehlt).
SELECT top_price,
XMLQUERY(
'for $cost in /buyer/contract/item/amount
where /buyer/name = $var1
return $cost'
PASSING BY VALUE
'A.Eisenberg' AS var1, buying_agents
RETURNING SEQUENCE BY VALUE EMPTY ON EMPTY)
FROM buyers;
Das zurückgelieferte Resultat kann per Referenz (BY REF) oder als kopierter Wert (BY VALUE) in SQL eingehen. Mit RETURNING CONTENT oder
RETURNING SEQUENCE wird festgelegt, ob der Rückgabetyp XML(CONTENT) oder XML(SEQUENCE) sein soll (siehe Abschnitt 8.4). Im Fall eines
leeren Ergebnisses wird mit dem Zusatz NULL ON EMPTY ein NULL-Wert
zurückgeliefert, bei EMPTY ON EMPTY eine leere Sequenz.
Mit der Funktion XMLCast können Typumwandlungen analog zu SQL
CAST vorgenommen werden. XMLCast hat die Syntax
XMLCAST ( value-expression AS type )
wobei einer der beiden Angaben sich auf einen XML-Typ beziehen muss.
Intern wird ein SQL CAST vorgenommen. Generell kann man an dieser
Stelle anmerken, dass sich das (überreichliche) Angebot an SQL-Typen
nicht immer gut mit den XML-Typen verträgt, wobei das XQuery-Datenmodell ursprünglich auf dem Infoset aufbaute und nur drei atomare
Datentypen (Boolean, double, string) kannte. Mit XQuery 1.0 und XPath
2.0 wurden deren Datenmodell an XML Schema angepasst und damit
wesentlich erweitert. Es bildet jetzt die Basis für SQL/XML.
Einführung in XML
170
8.4
XML-Datentyp
Wie in der Einleitung beschrieben, kann die Abspeicherung von XMLWerten in einer Tabellenspalte vom Typ XML erfolgen. Die zweite Auflage der SQL/XML-Norm von 2003 parametrisiert diesen Typ nun, wobei
die Typvariationen eine Hierarchie bilden.
XML(SEQUENCE) ist der allgemeinste Typ. Jeder XML-Wert in
SQL/XML ist entweder der SQL-Nullwert oder eine XQuery-Sequenz
und damit eine Ausprägung dieses Typs. Teile einer solchen Sequenz
könnten Elemente sein, Prozessorinstruktionen, Texte (Literale), numerische Werte, auch nicht-wohlgeformte Werte, alle durch Kommata
getrennt.
XML(CONTENT(ANY)) ist die zweite Variation unter XML(SEQUENCE). Jeder XML-Wert, der entweder einen Nullwert oder einen XQueryDokumentknoten bildet (einschließlich aller Kinder des Knotens), ist von
diesem Typ. Ausprägungen können auch nicht-wohlgeformte Dokumente
sein, etwa ein Dokument, das mehr als ein Wurzelelement besitzt. Das
kann als Zwischenresultat einer Abfrage entstehen, das später zu einem
wohlgeformten Dokumentknoten zurechtgestutzt wird.
XML(CONTENT(UNTYPED)) ist wiederum eine Untervariation von
XML(CONTENT(ANY)) und bezieht sich auf noch nicht durchgeführte
Schema-Validierungen.
XML(DOCUMENT(ANY)) ist im Gegensatz zu XML(CONTENT(ANY)) ein Dokumentknoten mit genau einem Wurzelelement
(und ggf. Prozessorinstruktionen und Kommentaren).
XML(DOCUMENT(UNTYPED) ist eine Ableitung sowohl des Supertyps XML(DOCUMENT(ANY)) als auch XML(CONTENT(UNTYPED)) und teilt deren Eigenschaften.
XML(CONTENT(XMLSCHEMA)) und XML(DOCUMENT(XMLSCHEMA)) sind Variationen der Typen XML(CONTENT(ANY)) bzw.
XML(DOCUMENT(ANY)). Der enthaltene XML-Wert muss Typanmerkungen eines XML-Schemas besitzen.
Kapitel 8 – SQL/XML
171
XML(SEQUENCE)
XML(CONTENT(ANY))
XML(CONTENT(UNTYPED))
XML(CONTENT(XMLSCHEMA))
XML(DOCUMENT(ANY))
XML(DOCUMENT(UNTYPED))
XML(DOCUMENT(XMLSCHEMA))
Abb. 8–1 Hierarchie der XML-Typen von SQL/XML
8.5
Prädikate
Der Standard enthält ein VALID-Prädikat, mit dem ein gegebener XMLWert auf Schemakonformität geprüft werden kann, wobei die Schemata
im Namensraum des Werts registriert sein müssen. Dies soll verhindern,
dass Fremde mittels einer Reihe von „Test-Schemata“ in einer Art von
Reverse Engineering den Metadatenaufbau eines Unternehmens herausbekommen können.
Die Syntax zum Testen allgemeiner XML-Werte lautet
xml-value IS [ NOT ] VALID
[ identity-constraint-option ]
[ validity-target ]
Neben dem VALID-Prädikat gibt es noch die folgenden weiteren:
■
DOCUMENT-Prädikat (ob der Wert ein wohlgeformtes XML-Dokument ist)
Einführung in XML
172
■
■
CONTENT-Prädikat für die XML(CONTENT)-Typen
XMLEXISTS-Prädikat (in Verbindung mit einer XQuery)
Wir gehen hierauf nicht weiter ein.
8.6
XMLTable
Wie auch in der Einleitung besprochen, ist die zweite Speicherform für
XML-Dokumente die „geschredderte“ Speicherung, d.h. hierarchische
Beziehungen werden aufgebrochen und in Tabellen in SQL abgespeichert. Diese Speicherform heißt bei IBM DB2 XML Extender dann XML
Collection Storage. Die SQL/XML-Norm definiert eine Pseudofunktion
XMLTable, die virtuelle SQL-Tabellen aus XML-Werten produziert.
XMLTable ist für Oracle verfügbar. Die Syntax lautet:
XMLTABLE ( [ namespace-declaration , ] xquery-expression
[ PASSING { BY REF | BY VALUE } argument-list ]
COLUMNS xmltbl-column-definitions )
ist ein Ausdruck genau wie bei XMLQuery oben,
d.h. als Literal zu spezifizieren Die verwendete Argumentliste übergibt die
aktuellen Parameter per Referenz oder als kopierte Werte.1
xquery-expression
Der COLUMNS-Teil ist eine gewöhnliche SQL-Spaltendefinition mit
Spaltenbezeichner, Typ und Pfadangabe für die Auswahl der Werte aus
dem Ergebnis der XQuery-Abfrage.
Zusätzlich ist es möglich, zur Erhaltung der Ordnung im Dokument
(Relationen sind Mengen, demnach sind die Tupel in Tabellen eigentlich
ungeordnet) eine Spalte für Ordinalzahlen einzuführen, in die der Positionswert des XML-Werts (was XQuery liefert) im Dokument automatisch
eingetragen wird. Die Syntax lautet:
column-name FOR ORDINALITY
Die Spalten der virtuellen Tabelle werden wie folgt generiert:
1. Laut [32] erfolgt die Übergabe nur per Referenz, das zugehörige Beispiel in [32] hat
aber den Zusatz PASSING :hv1 AS $dept BY VALUE.
Kapitel 8 – SQL/XML
column-name data-type [ BY REF | BY VALUE ]
[ default-clause ]
[ PATH xquery-expression ]
Das folgende Beispiel stammt aus [32]:
INSERT INTO EMPS (ID, NAME, SAL)
SELECT EMPNO, NAME, SALARY
FROM XMLTABLE(
'fn:doc(".../emps.xml")//emp'
COLUMNS
EMPNO INTEGER PATH 'badge'
NAME VARCHAR(50) PATH 'name'
SALARY DECIMAL(8,2) PATH 'comp/salary'
)
Damit beenden wir die Besprechung von SQL/XML.
173
175
9 XPointer und XLink
Die XML Linking Language (XLink) erlaubt die Beschreibung von einfachen unidirektionalen Verknüpfungsstrukturen, analog zu den einfachen
Hyperlinks in HTML heute, als auch komplexe Links. Die XML Pointer
Language (XPointer) kann interne Strukturen eines Dokuments als Teil
einer URI-Referenz adressieren. XPointer und XLink werden in den W3C
Dokumenten [13, 14] beschrieben. Der Text hier folgt dem Tutorial von
Melonfire [38].
Der große Erfolg des Web liegt in der Fähigkeit, Seiten mittels Verweisen (Links) verknüpfen zu können. Das Konzept aus den Sechziger
Jahren geht auf Ted Nelson, den Erfinder der Begriffe Hypertext und
Hypermedia, zurück. In HTML ist das Prinzip durch den Anker-Tag realisiert:
<a href="...">Dies ist ein Verweis</a>
Der HTML-Link hat allerdings eine Reihe von Nachteilen:
• jeder Link verweist von einer Ausgangsstelle zu genau einer Zielstelle; Mehrfachziele sind nicht möglich.
• der Verweis geschieht immer mit dem vordefinierten A-Tag.
• die Link-Definition (wohin der Link zeigen soll) steht im Quelldokument, für das man Schreibberechtigung braucht.
Die Anforderungen an XLink verlangen nun, dass
• Verweise wohlgeformte XML-Konstrukte sind
• lesbaren Klartext darstellen
Einführung in XML
176
• mit Informationen über Art, Titel, Ziel(e) und ggf. einem Verhalten
bei Verfolgen des Links versehen sind
• Mehrfachziele unterstützen
• ohne Schreibrechte – weder auf Quell- noch Zieldokument – definierbar sind
• beliebige Traversierungsrichtungen erlauben
• kompatibel mit HTML sind.
9.1
Das XLink-Konzept
XLink bricht die Angabe von Verweisen in drei Teile auf:
• die „Link Definition“ zur Angabe der Verbindungsart
• die „teilnehmenden Resourcen“ (items) die XLink verbindet; diese
können lokal (im selben Dokument) oder in einem fremden Dokument sein.
• „Traversierungsregeln“ oder „Pfeile (arcs)“, die angeben, in welche Richtung traversiert wird: „outbound“ (lokal nach entfernt),
„inbound“ (entfernt nach lokal) oder „third-party“ (entfernte
Resource nach entfernter Resource).
Ein Beispiel:
<?xml version="1.0"?>
<performers xmlns:xlink="http://www.w3.org/1999/xlink">
<item xlink:type="extended">
<!-- link definition (local) -->
<link xlink:type="resource" xlink:label="overview"
xlink:title="Information on Sinatra">
Frank Sinatra
</link>
<!-- link definitions (remote) - Sinatra's biography,
songs and articles -->
<link xlink:type="locator" xlink:href="bio.xml"
xlink:label="bio" xlink:title="Biography"/>
<link xlink:type="locator" xlink:href="songs.xml"
xlink:label="songs" xlink:title="Songs"/>
Kapitel 9 – XPointer und XLink
177
<link xlink:type="locator" xlink:href="press.xml"
xlink:label="press" xlink:title="Press articles"/>
<!-- local to remote arc - from name to biography -->
<arc xlink:type="arc" xlink:from="overview"
xlink:to="bio" xlink:show="replace"
xlink:actuate="onRequest"/>
<!-- remote to remote arc - from biography to song list
-->
<arc xlink:type="arc" xlink:from="bio" xlink:to="songs"
xlink:show="replace" xlink:actuate="onRequest"/>
<!-- remote to remote arc - from biography to press
archive -->
<arc xlink:type="arc" xlink:from="bio" xlink:to="press"
xlink:show="replace" xlink:actuate="onRequest"/>
</item>
</performers>
Das Beispiel zeigt konzeptuelle Verweise zwischen einem Künstler
(Frank Sinatra), seiner Biographie, seinen Liedern und einem Pressearchiv. Die Pfeile (arcs) geben die Traversierungsrichtungen an: vom
Künstler zu seiner Biographie, von der Biographie zur Liste der Lieder
und von der Biographie zum Archiv der Presseausschnitte.
Man beachte, dass Verweise nicht als Elemente, sondern als Attribute
(aus dem XLink-Namensraum) in beliebigen Elementen angegeben werden. Das wichtigste Attribut hat den Namen type mit der offensichtlichen
Bedeutung. Im Beispiel oben treten vier Typen auf: extended links,
resources, locators und arcs. Sie erscheinen in den Elementen item, link
und arc.
Je nach Wert des XLink-Attributs type werden für Zusatzangaben
eines oder mehrere Attribute hinzugefügt. Im Beispiel hat der Verweis
vom Typ locator das zusätzliche Attribut href, wohingegen XLinks für
den Typ arc die Attribute from und to besitzen können.
Einführung in XML
178
9.2
Einfache und erweiterte Links
XLink kennt zwei Grundarten von Verweisen: einfache und erweiterte
(simple and extended). Die einfachen sind an die gewohnten Anker-Tags
angelehnt mit klarer Verweisrichtung.
Beispiel:
<?xml version="1.0"?>
<performers xmlns:xlink="http://www.w3.org/1999/xlink">
<item xlink:type="simple" xlink:href="bio.xml">
Sinatra's biography</item>
<item xlink:type="simple" xlink:href="songs.xml">
Song list</item>
<item xlink:type="simple" xlink:href="press.xml">
Press clippings</item>
</performers>
Die gleichen Verweise in einer HTML-Seite sähen wie folgt aus:
<html>
<head>
</head>
<body>
<a href="bio.xml">Sinatra's biography</a>
<a href="songs.xml">Song list</a>
<a href="press.xml">Press clippings</a>
</body>
</html>
Erweiterte Links sind dagegen etwas ganz anderes und werden oft außerhalb der Dokumente gespeichert, die sie verbinden.
Im folgenden Beispiel werden Beziehungen zwischen Werdegang,
Gehalt und einer Beurteilung eines Angestellten modelliert.
Kapitel 9 – XPointer und XLink
179
appraisal
(Beurteilung)
name
(Ang.-name)
salary
(Gehalt)
resume
(Werdegang)
Abb. 9–1 Beziehungen im Beispiel (nach [38])
<?xml version="1.0"?>
<ext xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="extended">
<!-- starting point - employee's name (local) -->
<local xlink:type="resource" xlink:label="name"
xlink:title="John Doe"/>
<!-- employee's salary (remote) -->
<remote xlink:type="locator" xlink:href="salary.xml"
xlink:label="salary"
xlink:title="John Doe's salary information"/>
<!-- employee's last appraisal (remote) -->
<remote xlink:type="locator" xlink:href="appraisal.xml"
xlink:label="appraisal"
xlink:title="John Doe's last performance appraisal"/>
<!-- employee's resume (remote) -->
<remote xlink:type="locator" xlink:href="resume.xml"
xlink:label="resume"
xlink:title="John Doe's resume"/>
<!-- link name to resume -->
<arc xlink:type="arc" xlink:from="name" xlink:to="resume"
xlink:show="replace" xlink:actuate="onRequest"
xlink:title="Link from name to resume"/>
Einführung in XML
180
<!-- link name to current salary -->
<arc xlink:type="arc" xlink:from="name" xlink:to="salary"
xlink:show="replace" xlink:actuate="onRequest"
xlink:title="Link from name to salary"/>
<!-- link last appraisal to current salary-->
<arc xlink:type="arc" xlink:from="appraisal"
xlink:to="salary" xlink:show="replace"
xlink:actuate="onRequest"
xlink:title="Link from appraisal to salary"/>
<!-- these next two arcs set up a bidirectional link -->
<!-- link qualifications to salary -->
<arc xlink:type="arc" xlink:from="resume"
xlink:to="salary" xlink:show="replace"
xlink:actuate="onRequest"
xlink:title="Link from resume to salary"/>
<!-- link salary to qualifications -->
<arc xlink:type="arc" xlink:from="salary"
xlink:to="resume" xlink:show="replace"
xlink:actuate="onRequest"
xlink:title="Link from salary to resume"/>
</ext>
Ein erweiterter Verweis wird demnach als XML-Element realisiert, das
als Inhalt weitere XLink-Definitionen über teilnehmende lokale und entfernte Ressourcen und Pfeile enthält.
<ext xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="extended">
<!-- participating resources -->
</ext>
Die geschachtelten Elemente können vier Linktypen darstellen:
■
Resource. Dieser Linktyp stellt eine lokale Resource dar, die an dem
erweiterten Link teilnimmt. Üblicherweise enthält sie Inhalt, der als
Startpunkt für die Traversierung dient.
<local xlink:type="resource" xlink:label="name"
xlink:title="John Doe"/>
Kapitel 9 – XPointer und XLink
■
181
Locator. Dieser Linktyp stellt eine entfernte Resource dar (mit zusätzlichem "href" Attribut), die an dem erweiterten Link teilnimmt.
<remote xlink:type="locator" xlink:href="salary.xml"
xlink:label="salary"
xlink:title="John Doe's salary information"/>
■
Arc. Ein Pfeil gibt die Navigationsregeln zwischen „locators“ und
„resources“ an (mittels zusätzlichen "from" und "to" Attributen);
„arc“ ist der Hauptkonstrukt zur Richtungs- und Verhaltensangabe der
Verweistraversierung.
<arc xlink:type="arc"
xlink:from="name" xlink:to="salary"
xlink:show="replace" xlink:actuate="onRequest"
xlink:title="Link from name to salary"/>
„Arcs“ sind ganz praktisch, wenn es gilt, einen bi-direktionalen Link
einzurichten, z. B. A  B und B  A.
<arc xlink:type="arc"
xlink:from="resume" xlink:to="salary"
xlink:show="replace" xlink:actuate="onRequest"
xlink:title="Link from resume to salary"/>
<arc xlink:type="arc"
xlink:from="salary" xlink:to="resume"
xlink:show="replace" xlink:actuate="onRequest"
xlink:title="Link from salary to resume"/>
■
Allerdings sagt die XLink-Spezifikation ganz klar, dass Pfeile nicht
dupliziert werden können, d. h. eine „Von-nach-Beziehung“ kann nur
von genau einem Pfeil repräsentiert werden.
Title. Dieser Linktyp liefert zusätzliche, lesbare Informationen über
teilnehmende Ressourcen und kann als Alternative zum XLink-Attribut „title“ bei Elementen der Typen „extended“, „locator“ und „arc“
verwendet werden. Dies kann z. B. im Rahmen der Internationalisierung von Dokumenten genutzt werden.
Einführung in XML
182
<ext xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="extended">
<greeting xlink:type="title"
xml:lang="en">Hello!</greeting>
<greeting xlink:type="title"
xml:lang="fr">Bon jour!</greeting>
<greeting xlink:type="title"
xml:lang="it">Ciao!</greeting>
...
</ext>
9.3
Regeln für den Gebrauch der Attribute in XLink
Jedes Element, das einen XLink-Verweis definiert, muss ein type-Attribut enthalten.1 Gültige Werte dafür sind: simple, extended, locator,
arc, resource, title oder none.
Daneben sind neun zusätzliche Attribute möglich:
• Das href-Attribut zur Angabe einer URL einer entfernten
Resource; Pflicht für locator-Verweise. Zusätzlich zur URL der
Resource kann ein „fragment identifier“ angegeben werden, der
auf eine spezielle Stelle (z. B. angegeben durch ein id-Attribut) im
Zieldokument hinzeigt (siehe Besprechung zu XPointer).
• Das show-Attribut gibt an, wie das Ziel des Links präsentiert wird.
Mögliche Werte sind new (Anzeige im neuen Fenster); replace
(zeigt referenzierte Ressource im aktuellen Fenster, das gelöscht
wurde); embed (Anzeige in einem Teilbereich des gegenwärtigen
Fensters); other (Anzeige muss durch anwendungsspezifische
Angabe erfolgen) oder none (keine Anzeigeangabe).
• Das actuate-Attribut2 wird benutzt um anzugeben, wann ein Link
traversiert werden soll: onLoad (Anzeige der Resource, auf die der
Verweis zeigt, sobald vollständig geladen wurde); onRequest
(Anzeige nur wenn ausdrücklich verlangt, z. B. durch Mausklick);
other und none.
1. Ab XLink 1.1 ist das type-Attribut bei einfachen Links optional.
2. to actuate: in Gang bringen, auslösen
Kapitel 9 – XPointer und XLink
183
• Das label-Attribut wird benutzt, damit man den Link später bei
einem Pfeil identifizieren kann.
• Die from- und to-Attribute geben den Start- und Endpunkt eines
Pfeils an. Sie nutzen die label-Attribute in den Links zur Identifizierung dieser Punkte.
• Die role- und arcrole-Attribute referenzieren ein Dokument
(URL), das Informationen über die Rolle und den Zweck des Verweises enthält (vgl. Rollen von Beziehungen im ER-Modell). Eine
besondere Bedeutung hat das arcrole-Attribut in Verbindung mit
einer linkbase (siehe unten).
• Das title-Attribut, nicht zu verwechseln mit dem Attributwert
title für das type-Attribut, dient einer Beschreibung im Klartext.
Nicht alle Attribute (außer type bei XLink 1.0) werden überall benötigt.
Die Regeln lauten:
1. Ein einfacher Link kann die Attribute href und optional show und
actuate enthalten.
2. Einfache und erweiterte Links können optional role- und titleAttribute enthalten, um den Zweck zu erläutern.
3. Ein locator-Link muss ein href-Attribut enthalten zur Angabe
der URL der entfernten Resource. Der Verweis enthält meist auch
das optionale label-Attribut als Identifikation für Pfeile (arcs).
4. Aus dem gleichen Grund enthält ein resource-Link meist ein label-Attribut.
5. Ein Pfeil (arc) enthält typischerweise die optionalen from- und toAttribute, ferner optional show und actuate, sowie optional das
arcrole-Attribute, wenn der Pfeil mit einer linkbase auftritt.
9.4
Beispiel
„Warum einfach, wenn es auch kompliziert geht“, lautet ein alter Spruch.
<item xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="simple" xlink:href="bio.xml">
Sinatra's biography</item>
Einführung in XML
184
Als erweiterten Link mit gleicher Funktionalität erhält man:
<item xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="extended">
<!-- name -->
<loc xlink:type="resource" xlink:label="local"
xlink:title="Frank Sinatra">Frank Sinatra</loc>
<!-- bio -->
<rem xlink:type="locator" xlink:href="bio.xml"
xlink:label="remote" xlink:title="Biography"/>
<!-- local to remote arc - from name to biography -->
<arc xlink:type="arc" xlink:from="local"
xlink:to="remote"/>
</item>
Man beachte also, dass ein einfacher Link sowohl das entfernte Ziel
benennt, als auch die unidirektionale Traversierung. Ein Lokator (locator)
gibt dagegen nur das Ziel (die entfernte Resource) an und überlässt die
Traversierung dem Pfeil (arc).
9.5
Linkbase
Der Linkbase-Konstrukt ist eine Datenbasis von Links, d. h. es geht um
die Möglichkeit, mehrere Verweisziele in einem separaten XML-Dokument zusammenzufassen und vom Quelldokument einen Verweis auf
diese Datei zu setzen.
Das folgende Beispiel verknüpft ein Land (USA) mit seinen Bundesstaaten.
<ext xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="extended">
<!-- start from here -->
<link xlink:type="resource" xlink:label="country"
xlink:title="Country">United States</link>
<!-- linkbase containing links to states -->
<directory xlink:type="locator" xlink:href="states.xml"
Kapitel 9 – XPointer und XLink
185
xlink:label="states"
xlink:title="States within country"/>
<!-- special linkbase arc with arcrole attribute -->
<arc xlink:type="arc" xlink:from="country"
xlink:to="states"
xlink:arcrole="http://www.w3.org/1999/
xlink/properties/linkbase"
xlink:show="replace" xlink:actuate="onRequest"/>
</ext>
Ein XLink-Prozessor würde bei einem solchen Verweis die Linkbase aufsuchen (hier states.xml) und dann nacheinander die dort enthaltenen
Verweise gemäß der dort abgelegten Traversierungsregeln verfolgen. Der
Standard sieht auch die Verkettung von Linkbases vor. Die Anwendung
sollte zirkuläre Verweisketten erkennen und entsprechend beachten.
9.6
XPointer
XPointer-Verweise [13, 40] auf Fragmente in entfernten Dokumenten
sind wieder ähnlich den bekannten HTML-Ankern. Grundsätzlich gibt es
zwei Formen: Kurzhand- und Schemaform.
• document#id („shorthand form“)
• document#scheme(...) („scheme-based form“)
Die Besprechung hier folgt der Beispielsammlung von zvon.org in [39].
Die Kurzhand-Form beruht auf dem ID-Mechanismus in XML. Das
Ziel wird über den Wert eines ID-Attributs oder ID-Elements bestimmt.
Einführung in XML
186
XPointer:
b1
<!DOCTYPE AAA
[
<!ELEMENT AAA (BBB+)>
<!ELEMENT BBB EMPTY>
<!ATTLIST BBB id ID #REQUIRED>
]
>
<AAA>
<BBB id="b1"/>
<BBB id="b2"/>
</AAA>
Schemabasierte Formen kennen verschiedene Mechanismen zur Identifikation passender Dokumentteile. Das Beispiel unten verwendet das element()-Schema (vgl. W3C Recommendation [40]).
XPointer:
element(/1/1)
<AAA>
<BBB id="b1"/>
<BBB id="b2"/>
</AAA>
9.6.1 Kurzform mit ID-Attribut
Das Element BBB hat ein id-Attribut, das als ID-Typ in einem XMLSchema definiert ist.
Kapitel 9 – XPointer und XLink
XPointer:
187
b1
Matches element with ID "b1".
<AAA
xsi:noNamespaceSchemaLocation="schema.xsd"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance">
<BBB id="b1"/>
<BBB id="b2"/>
</AAA>
XML Schema
(schema.xsd): <xsd:schema
xmlns:xsd=
"http://www.w3.org/2001/XMLSchema"
version="1.0">
<xsd:element name="AAA">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="BBB"
maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="id"
type="xsd:ID"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Einführung in XML
188
9.6.2 Kurzform mit schemabestimmtem ID-Element
The Element BBB hat ein id-Element das vom ID-Typ ist, wie durch das
XML Schema definiert.
XPointer:
b1
Matches element with ID "b1".
<AAA
xsi:noNamespaceSchemaLocation="schema.xsd"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance">
<BBB><id>b1</id></BBB>
<BBB><id>b2</id></BBB>
</AAA>
XML Schema
(schema.xsd): <xsd:schema
xmlns:xsd=
"http://www.w3.org/2001/XMLSchema"
version="1.0">
<xsd:element name="AAA">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="BBB"
maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id"
type="xsd:ID"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Kapitel 9 – XPointer und XLink
189
9.6.3 Kurzform mit ID-Attribut und DTD-Vereinbarung
XPointer:
b2
Matches element with ID "b2".
<!DOCTYPE AAA
[
<!ELEMENT AAA (BBB+)>
<!ELEMENT BBB EMPTY>
<!ATTLIST BBB id ID #REQUIRED>
]
>
<AAA>
<BBB id="b1"/>
<BBB id="b2"/>
</AAA>
9.6.4 Schemabasierte Pointer: Child Sequence
Eine „child sequence“ ist ein absoluter Pfad zu dem ausgewählten Element. Der Pfad ist eine Folge von Schrägstrichen und Zahlen, beginnend
mit „/“. Jede Zahl n bestimmt das n-te Kind des vorher bestimmten Elements.
XPointer:
Corresponding XPath:
element(/1)
/*[1]
Matches root element
<AAA>
<BBB>
<CCC/>
<CCC/>
</BBB>
<BBB>
<CCC/>
<CCC/>
</BBB>
</AAA>
Einführung in XML
190
XPointer:
Corresponding
XPath:
element(/1/2)
/*[1]/*[2]
Matches the second child of the root
element.
<AAA>
<BBB>
<CCC/>
<CCC/>
</BBB>
<BBB>
<CCC/>
<CCC/>
</BBB>
</AAA>
XPointer:
Corresponding
XPath:
element(/1/2/1)
/*[1]/*[2]/*[1]
Matches the first child of the second child
of the root element.
<AAA>
<BBB>
<CCC/>
<CCC/>
</BBB>
<BBB>
<CCC/>
<CCC/>
</BBB>
</AAA>
Kapitel 9 – XPointer und XLink
191
9.6.5 Elementauswahl über ID
Das Element BBB hat ein id-Attribut, das vom Typ ID ist, z. B. nach DTDAngabe.
element(b1)
XPointer:
Corresponding id('b1')
XPath:
Matches element with ID "b1".
<!DOCTYPE AAA
[
<!ELEMENT AAA (BBB+)>
<!ELEMENT BBB EMPTY>
<!ATTLIST BBB id ID #REQUIRED>
]
>
<AAA>
<BBB id="b1"/>
<BBB id="b2"/>
</AAA>
9.6.6 ID kombiniert mit „Child Sequence“
Die „child sequence“ beginnt bei dem Element, dessen ID-Wert angegeben wurde, nicht an der Wurzel.
element(b1/1)
XPointer:
Corresponding id('b1')/*[1]
XPath:
Matches the first child of the element with
ID "b1".
Einführung in XML
192
<!DOCTYPE AAA
[
<!ELEMENT AAA
<!ELEMENT BBB
<!ATTLIST BBB
<!ELEMENT CCC
]
>
<AAA>
<BBB id="b0">
<CCC/>
<CCC/>
</BBB>
<BBB id="b1">
<CCC/>
<CCC/>
</BBB>
</AAA>
(BBB+)>
(CCC+)>
id ID #REQUIRED>
EMPTY>
193
10 Document Object Model
Das Document Object Model (DOM) ist eine plattform- und sprachunabhängige, baumartige interne Darstellung eines XML-Dokuments. Der
Zugriff auf ein Dokument wird durch die DOM-Schnittstelle festgelegt.
DOM ist in sog. Levels unterteilt. DOM-Level 1 [15] behandelt die Darstellung von XML- und HTML-Dokumenten im Allgemeinen. DOMLevel 2 [16] enthält weitere Teile für Namensräume, CSS-Stylesheets und
Ereignisunterstützung. Level 3 [17] bietet zusätzlich Laden, Speichern,
Parsen, Validieren und Serialisieren von Dokumenten und den Einsatz
von XPath.
Die DOM-Schnittstelle wird durch die IDL (Interface Definition Language) definiert. In ihr werden die Methoden beschrieben, die jedes Interface vorweisen muss, nicht aber die Klassen, die für eine bestimmte Programmiersprache implementiert werden sollen. Deshalb wird für jede
Programmiersprache eine sog. Sprachanbindung festgelegt. Wir verwenden hier die Java-Sprachanbindung, die ebenfalls im Standard unter
http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/java-
zur Verfügung steht. Das Paket, das diese
Schnittstelle definiert, ist org.w3c.dom.
language-binding.html
10.1 Erzeugen eines DOM-Baums mit dem DOM-Parser
Bis einschließlich Level 2 legt die Schnittstelle nicht fest, wie man aus
einem XML-Dokument einen DOM-Baum erzeugt. In der Regel ist dies
die Aufgabe desjenigen, der die Schnittstelle für eine bestimmte Programmiersprache implementiert. Wir verwenden hier Xerces [19], der einen
194
Einführung in XML
sog. DOM-Parser zur Verfügung stellt. Im nächsten Beispiel lesen wir ein
XML-Dokument aus einer Datei ein und erzeugen daraus ein DocumentObjekt, welches das XML-Dokument darstellt:
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
public class domTest {
public void performDemo (String uri) {
DOMParser parser = new DOMParser();
try {
parser.parse(uri);
Document document = parser.getDocument();
} catch (Exception e) {
System.out.println("Error during parsing");
}
}
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("usage domTest uri");
System.exit(0);
}
domTest domtest = new domTest();
domtest.performDemo(args[0]);
}
}
Den erzeugten DOM-Baum (gegeben durch das Objekt Document) können wir mithilfe der Methoden der DOM-Schnittstelle dynamisch verändern und schließlich wieder als (neues) XML-Dokument serialisieren.
10.2 Darstellung des Dokuments
DOM stellt ein Dokument durch eine Baumstruktur dar. Dabei wird jeder
Knoten durch ein Node-Objekt repräsentiert, woraus speziellere Knotentypen abgeleitet werden können. Einige Knotenarten können weitere
Unterknoten haben, andere sind Blattknoten. Wir geben hier alle Knotentypen mit ihren möglichen Unterknoten an:
Kapitel 10 – Document Object Model
195
■ Document
Element (Anzahl maximal eins), ProcessingInstruction, Comment,
DocumentType (Anzahl maximal eins)
■ DocumentFragment
Element, ProcessingInstruction, Comment, Text, CDATASection,
EntityReference
■ DocumentType
keine Sohnknoten
■ EntityReference
Element, ProcessingInstruction, Comment, Text, CDATASection,
EntityReference
■ Element
Element, Text, Comment, ProcessingInstruction, CDATASection,
EntityReference
■ Attr
Text, EntityReference
■ ProcessingInstruction
keine Sohnknoten
■ Comment
keine Sohnknoten
■ Text
keine Sohnknoten
■ CDATASection
keine Sohnknoten
■ Entity
Element, ProcessingInstruction, Comment, Text, CDATASection,
EntityReference
■ Notation
keine Sohnknoten
196
Einführung in XML
Abb. 10–1 Ein XML-Dokument als DOM-Baum
Jedes dieser Interfaces repräsentiert den entsprechenden Knotentyp aus
dem XML-Dokument und stellt eine Liste von Methoden zur Verfügung,
die für diesen Knoten sinnvoll sind. Z. B. liefert die Methode getNodeValue() für einen Text-Knoten das entsprechende Textelement als String
zurück.
Weiterhin spezifiziert DOM die Interfaces NodeList und NamedNodeMap. NodeList stellt eine geordnete Liste von Node-Objekten dar. Zum
Beispiel alle Sohnknoten eines Elements. NamedNodeMap stellt eine ungeordnete Menge von Node-Objekten dar, die über ihre Namen referenziert
werden. Beispiel: alle Attribute eines Elements. Beide Listen sind sog.
live-Objekte, das heißt, Änderungen an dem DOM-Baum werden ohne
weitere Aktualisierung in den Listen reflektiert.
10.3 Beschreibung der Objekte und Methoden
Im Standard werden die Eigenschaften und Methoden aller DOM-Objekte
beschrieben. Der Standard [15, 16] spricht dabei von DOM Core Interfaces. Wir geben hier eine Beschreibung der wichtigsten Teile. Weitere
Details können dem Standard entnommen werden. Bei der Arbeit mit
einer bestimmten Sprachschnittstelle wird man immer wieder auf deren
Dokumentation zugreifen müssen, um Details wie Rückgabewert, Fehler-
Kapitel 10 – Document Object Model
197
fälle und Argumente zu bekommen. Für unsere Beispiele verwenden wir
die Dokumentation von Xerces [20] auf der Seite http://
xml.apache.org/xerces2-j/javadocs/api/org/w3c/dom/packagesummary.html.
10.3.1 DOMException
Repräsentiert den Auftritt eines Fehlers. Dabei werden bestimmte Fehlercodes festgelegt. So signalisiert z. B. die Konstante DOMException.INDEX_SIZE_ERR die Verwendung eines nicht passenden Indexes.
10.3.2 DOMImplementation
Dieses Interface repräsentiert die Eigenschaften einer Implementierung
und soll einer Anwendung ermöglichen, die Unterstützung bestimmter
Funktionen abzufragen. Die Interface-Funktion enthält als einzige
Methode:
boolean hasFeature(String feature, String version)
Zulässige Angaben für feature in DOM-Level 1 sind XML und HTML
(Großschreibung!).
10.3.3 DocumentFragment
Dieses Interface dient zur Darstellung von Dokumentteilen. Nützlich z. B.
für die Implementierung von Funktionen wie Cut&Paste. Wir gehen auf
seine Methoden hier nicht ein.
10.3.4 Document
Dieses Interface repräsentiert das gesamte XML-Dokument. Da alle Knoten innerhalb eines Document-Nodes eine Bedeutung haben, deklariert
dieses Interface die Methoden zum Erzeugen aller Knotentypen. Erzeugte
Knoten können dann innerhalb anderer Knoten eingefügt werden (siehe
weiter unten). Document deklariert die folgenden Methoden:
Einführung in XML
198
public DocumentType getDoctype()
public DOMImplementation getImplementation()
public Element getDocumentElement()
public Element createElement(String tagName)
public DocumentFragment createDocumentFragment()
public Text createTextNode(String data)
public Comment createComment(String data)
public CDATASection createCDATASection(String data)
public ProcessingInstruction
createProcessingInstruction(String target, String data)
public Attr createAttribute(String name)
public EntityReference createEntityReference(String name)
public NodeList getElementsByTagName(String tagname)
Die Methode getDocType() liefert ein Objekt vom Typ DocumentType,
das die DOCTYPE-Deklaration im Dokument darstellt. Fehlt diese Deklaration im Dokument so wird null zurückgeliefert. Die create-Methoden
liefern eine Instanz des jeweiligen Knotentyps zurück. getElementsByTagName() liefert eine geordnete Liste aller Elemente im Dokument mit
dem Namen tagname. getDocumentElement() liefert den Wurzelknoten
als Element-Objekt zurück.
10.3.5 Node
Dieses Interface repräsentiert alle Knotentypen. Die folgenden Konstanten repräsentieren den jeweiligen Knotentyp eines Node-Objekts:
ELEMENT_NODE
ATTRIBUTE_NODE
TEXT_NODE
CDATA_SECTION_NODE
ENTITY_REFERENCE_NODE
ENTITY_NODE
PROCESSING_INSTRUCTION_NODE
COMMENT_NODE
DOCUMENT_NODE
DOCUMENT_TYPE_NODE
DOCUMENT_FRAGMENT_NODE
NOTATION_NODE
=
=
=
=
=
=
=
=
=
=
=
=
1;
2;
3;
4;
5;
6;
7;
8;
9;
10;
11;
12;
Node deklariert die folgenden Methoden:
Kapitel 10 – Document Object Model
199
■ public String getNodeName()
liefert den Namen eines Knotens zurück.
■ public String getNodeValue()
liefert den Wert eines Knotens zurück (siehe weiter unten).
■ public void setNodeValue(String nodeValue)
setzt den Wert eines Knotens.
■ public short getNodeType()
liefert den Knotentyp zurück.
■ public Node getParentNode()
liefert einen Verweis auf den Vaterknoten.
■ public NodeList getChildNodes()
liefert eine geordnete Liste aller Unterknoten.
■ public Node getFirstChild()
liefert den ersten Unterknoten zurück.
■ public Node getLastChild()
liefert den letzten Unterknoten zurück.
■ public Node getPreviousSibling()
liefert den unmittelbar davorliegenden (Bruder-)Knoten zurück.
■ public Node getNextSibling()
liefert den unmittelbar nächsten (Bruder-)Knoten zurück.
■ public NamedNodeMap getAttributes()
liefert eine ungeordnete Liste aller Attribute.
■ public Document getOwnerDocument()
liefert einen Verweis auf das Document-Objekt des Dokuments zu dem
Node gehört.
■ public Node insertBefore(Node newChild, Node refChild)
fügt newChild vor refChild ein.
■ public Node replaceChild(Node newChild, Node oldChild)
ersetzt oldChild durch newChild.
■ public Node removeChild(Node oldChild)
entfernt oldChild.
■ public Node appendChild(Node newChild)
fügt newChild als letzten Unterknoten ein.
Einführung in XML
200
■ public boolean hasChildNodes()
liefert true, falls Node Unterknoten hat.
■ public Node cloneNode(boolean deep)
erzeugt eine Kopie von Node.
Da Node verschiedene Knotentypen repräsentiert, hängt das Verhalten der
Methoden vom Typ des Knotens ab. So erzeugen einige dieser Methoden
sogar einen Fehler, falls sie bei dem jeweiligen Knoten keinen Sinn
machen, z. B. der Aufruf der Methode appendChild() bei einem TextKnoten. Andere Methoden liefern unterschiedliche Werte in Abhängigkeit des Knotentyps. Die Werte für nodeName, nodeValue und attributes (sowohl bei get- als auch set-Methoden) sind für einige Knotentypen in der folgenden Tabelle aufgelistet:
Interface
nodeName
nodeValue
attributes
Attr
Attributname
Attributwert
null
CDATASection
#cdata-section
Inhalt des
CDATA-Abschnitts
null
Comment
#comment
Inhalt des
Kommentars
null
Document
#document
null
null
Element
Elementname
null
Liefert
NamedNodeMap
Text
#text
Textinhalt
null
10.3.6 Attr
repräsentiert einen Attribut-Node. Zusätzlich zu den vererbten
Methoden von Node werden die folgenden Methoden deklariert:
Attr
Kapitel 10 – Document Object Model
201
■ String getName()
liefert den Namen des Attributs zurück.
■ boolean getSpecified()
liefert true, falls das Attribut explizit im Dokument spezifiziert ist
(kein Standardwert), sonst false.
■ String getValue()
liefert den Wert des Attributs zurück.
■ void setValue(String value)
setzt den Wert des Attributs auf value.
10.3.7 Element
repräsentiert einen Element-Node und stellt zusätzlich die folgenden Methoden zur Verfügung:
Element
■ String getTagName()
liefert den Elementnamen zurück.
■ String getAttribute(String name)
liefert den Wert des Attributs mit dem Namen name zurück. null, falls
nicht vorhanden.
■ void setAttribute(String name, String value)
setzt den Wert des Attributs name auf value. name wird neu erzeugt,
falls es noch nicht existiert.
■ void removeAttribute(String name)
löscht das Attribut name aus der Attributliste des Elements.
■ Attr getAttributeNode(String name)
liefert ein Attr-Objekt für das Attribut name zurück. null, falls name
nicht existiert.
■ Attr setAttributeNode(Attr newAttr)
fügt einen neuen Attribut-Node als Unterknoten des Elements ein.
■ Attr removeAttributeNode(Attr oldAttr)
entfernt den Attributknoten oldAttr aus der Liste der Attributknoten
des Elements.
202
Einführung in XML
■ NodeList getElementsByTagName(String name)
liefert eine geordnete Liste aller Nachfahrenelemente mit dem Namen
name.
10.3.8 Text und Comment
repräsentiert Zeichenketten innerhalb von XML-Dokumenten,
sowohl als Inhalt von Elementen, als auch als Wert von Attributen. Falls
der Text keinen weiteren Markup enthält, wird der gesamte Text als ein
einziges Text-Objekt dargestellt. Andernfalls kann es auch benachbarte
Textknoten geben, die sich mit normalize wieder verschmelzen lassen.
Text
DOM definiert das Interface CharacterData. Es dient als eine Oberklasse für alle Objekte, die Zeichenketten repräsentieren. Die meisten
Methoden von Text werden daher von CharacterData geerbt. Wir
erwähnen hier die folgenden Methoden:
■ String getData()
liefert den Inhalt als String zurück.
■ void setData(String data)
setzt den Wert auf data.
■ int getLength()
liefert die Länge der Zeichenkette zurück.
Das Interface Comment wird ebenfalls von CharacterData abgeleitet. Es
enthält keine zusätzlichen Methoden. Jedoch verhalten sich die CharacterData-Methoden bei Knoten vom Typ Comment naturgemäß anders
als bei Text-Knoten (siehe oben).
10.3.9 NodeList
NodeList dient
zur Aufnahme von mehreren Knoten in einer geordneten
Liste. Dadurch können z. B. alle Kindknoten eines Elements in einer
Schleife durchlaufen werden. Z. B. liefert die Methode Node.getChildNodes() alle Unterknoten eines beliebigen Knotens als ein NodeListObjekt zurück.
NodeList stellt die folgenden Methode zur Verfügung:
Kapitel 10 – Document Object Model
203
■ Node item(int i)
liefert den i-ten Eintrag der Liste als Node zurück.
■ int getLength()
liefert die Anzahl der Einträge der Liste zurück.
Durch die beiden Methoden kann man die Liste in einer Schleife durchlaufen und die einzelnen Einträge bearbeiten (siehe Beispiel weiter
unten).
10.3.10 NamedNodeMap
stellt eine ungeordnete Kollektion von Node-Objekten
dar, die über ihre Namen referenziert werden, z. B. alle Attribute eines
Elements. Es bietet die folgenden Methoden:
NamedNodeMap
■ Node getNamedItem(String name)
liefert den Knoten mit dem Namen name zurück.
■ Node setNamedItem(Node node)
fügt den neuen Knoten node zu der Kollektion hinzu.
■ Node removeNamedItem(String name)
entfernt den Knoten mit dem Namen name aus der Kollektion.
■ Node item(int i)
liefert den i-ten Eintrag der Kollektion als Node zurück.
■ int getLength()
liefert die Anzahl der Einträge der Kollektion zurück.
10.4 Beispiel: Serialisieren eines DOM-Baumes
Im folgenden Beispiel entwickeln wir die Klasse DOMSerializer, die
einen DOM-Baum als XML-Dokument serialisiert.1 Dabei behandeln wir
nur Element-, Text- und Attributknoten. Kommentare, Prozessoranweisungen usw. werden ignoriert.
1. Natürlich stellen Xerces und Xalan solche Klassen zur Verfügung, die eine viel
schönere Ausgabe produzieren.
Einführung in XML
204
class DOMSerializer {
private int indent;
private Node rootNode;
/** Konstuktor */
public DOMSerializer(Document doc) {
indent = 0;
rootNode = (Node) doc;
}
/**
* Schreibt eine Zeile in OutputStream. Davor werden
* 3*indent leere Zeichen geschrieben.
*/
private void writeIndentLine(String line,PrintStream to){
for (int i = 0; i < indent ; i++) {
to.print("
");
}
to.println(line);
}
/**
* Erzeuge aus einem DOM-Baum eine (XML-)Textausgabe.
*/
private void printNode(Node node, PrintStream to) {
switch (node.getNodeType()) {
case Node.DOCUMENT_NODE:
Document doc = (Document) node;
printNode(doc.getDocumentElement(), to);
break;
case Node.ELEMENT_NODE:
String name = node.getNodeName();
String elementStr = "<" + name;
NamedNodeMap attrs = node.getAttributes();
for(int i = 0; i < attrs.getLength();i++) {
Node current = attrs.item(i);
elementStr = elementStr + " " +
current.getNodeName() + "=\"" +
current.getNodeValue() + "\"";
}
elementStr = elementStr + ">";
writeIndentLine(elementStr, to);
NodeList children = node.getChildNodes();
Kapitel 10 – Document Object Model
205
if(children != null) {
indent++;
for (int i = 0; i < children.getLength(); i++) {
printNode(children.item(i), to);
}
indent--;
}
writeIndentLine("</" + name + ">", to);
break;
case Node.TEXT_NODE:
writeIndentLine(node.getNodeValue(), to);
break;
case Node.PROCESSING_INSTRUCTION_NODE:
break;
case Node.ENTITY_REFERENCE_NODE:
break;
case Node.DOCUMENT_TYPE_NODE:
break;
}
}
public void serialize(PrintStream to) {
printNode(rootNode, to);
}
}
Zum Testen dieser Klasse schreiben wir ein einfaches Programm, das ein
XML-Dokument aus einer Datei in einen DOM-Baum umwandelt und
dieses schließlich (mit einfacher Formatierung) auf der Standardausgabe
ausgibt:
import
import
import
import
import
import
java.io.PrintStream;
org.w3c.dom.Document;
org.w3c.dom.Node;
org.w3c.dom.NodeList;
org.w3c.dom.NamedNodeMap;
org.apache.xerces.parsers.DOMParser;
class DOMSerializer { ... }
public class XMLWriter {
private Document xmltoDom (String uri) {
DOMParser parser = new DOMParser();
Einführung in XML
206
Document res = null;
try {
parser.parse(uri);
res = parser.getDocument();
} catch (Exception e) {
System.out.println("Error during parsing");
}
return res;
}
public void printOut(String uri) {
Document xmlDom = xmltoDom(uri);
DOMSerializer serializer = new DOMSerializer(xmlDom);
serializer.serialize(System.out);
}
public static void main(String[] args) {
if (args.length == 1) {
XMLWriter xmlW = new XMLWriter();
xmlW.printOut(args[0]);
} else {
System.out.println("usage XMLWriter uri");
System.exit(1);
}
}
}
10.5 Beispiel: Erzeugen eines DOM-Baumes
In diesem Beispiel erzeugen wir aus einer internen Datenstruktur Mitareinen neuen DOM-Baum, und dann serialisieren wir diesen
DOM-Baum in einer externen Datei.
beiter
import
import
import
import
import
import
import
import
import
import
java.io.File;
java.io.FileOutputStream;
java.io.PrintStream;
java.io.IOException;
org.w3c.dom.Document;
org.w3c.dom.Node;
org.w3c.dom.Element;
org.w3c.dom.Attr;
org.w3c.dom.NodeList;
org.w3c.dom.NamedNodeMap;
Kapitel 10 – Document Object Model
207
import org.apache.xerces.dom.DocumentImpl;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.TransformerException;
import
javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
public class XMLServerSim {
private static final String SHARE_FILE_NAME =
"share.xml";
// Wir verwenden die etwas bessere Serialisierungsmethode
private void printNode (Node node, PrintStream to)
throws IOException, TransformerConfigurationException,
TransformerException {
TransformerFactory tfactory =
TransformerFactory.newInstance();
Transformer serializer = tfactory.newTransformer();
serializer.setOutputProperty(OutputKeys.METHOD,
"xml");
serializer.setOutputProperty(OutputKeys.INDENT,
"yes");
serializer.setOutputProperty(OutputKeys.ENCODING,
"ISO-8859-1");
serializer.transform(new DOMSource(node),
new StreamResult(to));
}
public void start() {
Mitarbeiter k1 = new Mitarbeiter ("1", "Ahmad",
"Morad");
Mitarbeiter k2 = new Mitarbeiter ("2", "Wegner",
"Lutz");
Mitarbeiter k3 = new Mitarbeiter ("3", "Fisher",
"Burkhardt");
try {
File outputFile = new File(SHARE_FILE_NAME);
PrintStream to =
new PrintStream(new FileOutputStream(outputFile));
Document mitarbDom = new DocumentImpl();
Einführung in XML
208
Element mitarbeiter =
mitarbDom.createElement("mitarbeiter");
mitarbeiter.appendChild(k1.asNode(mitarbDom));
mitarbeiter.appendChild(k2.asNode(mitarbDom));
mitarbeiter.appendChild(k3.asNode(mitarbDom));
mitarbDom.appendChild(mitarbeiter);
printNode((Node) mitarbDom, to);
to.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
XMLServerSim server = new XMLServerSim();
server.start();
}
}
Die Klasse Mitarbeiter könnte wie folgt aussehen:
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class Mitarbeiter {
private String id, name, vorname;
public Mitarbeiter (String id, String name, String
vorname) {
this.id = id;
this.name = name;
this.vorname = vorname;
}
public Element asNode (Document doc) {
Element mitarb = doc.createElement("mitarb");
Element newElement = null;
newElement = doc.createElement("id");
newElement.appendChild(doc.createTextNode(this.id));
mitarb.appendChild(newElement);
newElement = doc.createElement("name");
newElement.appendChild(
doc.createTextNode(this.name));
Kapitel 10 – Document Object Model
209
mitarb.appendChild(newElement);
newElement = doc.createElement("vorname");
newElement.appendChild(
doc.createTextNode(this.vorname));
mitarb.appendChild(newElement);
return mitarb;
}
}
Das erzeugte XML-Dokument sieht dann wie folgt aus (siehe printNode()):
<?xml version="1.0" encoding="ISO-8859-1"?>
<mitarbeiter>
<mitarb>
<id>1</id>
<name>Ahmad</name>
<vorname>Morad</vorname>
</mitarb>
<mitarb>
<id>2</id>
<name>Wegner</name>
<vorname>Lutz</vorname>
</mitarb>
<mitarb>
<id>3</id>
<name>Fisher</name>
<vorname>Burkhardt</vorname>
</mitarb>
</mitarbeiter>
211
11 SAX
SAX (Simple API for XML) ist eine XML-Schnittstelle, die eine sequentielle Abarbeitung eines Dokuments ermöglicht. Die Abarbeitung erfolgt
ohne dass zunächst das ganze Dokument in den Hauptspeicher eingelesen
wird. Für viele Aufgaben ist dieser Ansatz effizienter als die DOMSchnittstelle, die immer das ganze Dokument vor der Verarbeitung lädt.
Im Gegensatz zur DOM-Schnittstelle ist SAX durch die Zusammenarbeit mehrerer Entwickler aus der Open-Source-Gemeinde entstanden.
Trotzdem gilt SAX als Quasi-Standard und wird von den meisten Parsern
unterstützt. Die offizielle Seite für den SAX-Standard ist:
http://sax.sourceforge.net [43]. Dort findet man ebenfalls die JavaDokumentation für SAX. Ein gängiges Textbuch stammt von David
Brownell [42].
Das Hauptprinzip von SAX ist das Auffassen eines XML-Dokuments
als Folge von Ereignissen. Der Anwendungsentwickler implementiert für
diese Ereignisse die entsprechenden Callback-Methoden und erreicht
damit, dass der Parser diese Methoden beim Eintritt dieser Ereignisse aufruft.
Um zu verstehen, wie ein XML-Dokument aus Sicht der SAXSchnittstelle wirkt, betrachten wir das folgende Dokument:
<?xml version="1.0"?>
<doc>
<para>Hello, world!</para>
</doc>
SAX stellt das Dokument als Folge von Ereignissen wie folgt dar:
212
Einführung in XML
start document
start element: doc
start element: para
characters: Hello, world!
end element: para
end element: doc
end document
Zur Implementierung der Ereignismethoden ist jeweils ein sog. Handler
zu implementieren. Ein Handler ist eine Menge von Callbacks, die der
SAX-Parser bei dem Eintritt von bestimmten Ereignissen aufruft.
Der SAX-2.0-Standard definiert vier verschiedene Handler-Interfaces:
ContentHandler, ErrorHandler (Fehlerbehandlung), DTDHandler
(Meldungen über Notationen und nicht-geparste Entitäten) und EntityResolver (Auflösung von externen Entities). Wir beschränken uns hier
auf die beiden wichtigeren Handler: ContentHandler und ErrorHandler.
In der Regel entwickelt man eine Klasse, die eine Methode aus diesen
Handler-Interfaces implementiert nach dem Muster:
class MyContentHandler implements ContentHandler {...}
Man registriert dann diese Handler-Implementierung bei dem SAX-Parser mit den Methoden setContentHandler(), setErrorHandler() usw.
und erreicht damit, dass die implementierten Methoden beim Eintritt von
bestimmten Ereignissen vom SAXParser aufgerufen werden.
Das Verwenden eines SAX-Parsers geschieht in der Regel nach dem
folgenden Muster:
// Implementieren der Handler-Klassen (Content, Error etc.)
class MyContentHandler implements ContentHandler {...}
...
// Erzeugen des SAX-Parsers
XMLReader xr = new SAXParser();
// Erzeugen des ContentHandlers
MyContentHandler conHandler = new MyContentHandler();
// Erzeugen des ErrorHandlers
Kapitel 11 – SAX
213
MyErrorHandler errHandler = new MyErrorHandler();
// Registrieren der Handler bei dem SAX-Parser
xr.setContentHandler(conHandler);
xr.setErrorHandler(errHandler);
// Den SAX-Parser aufrufen
xr.parse(new InputSource(dateiname));
...
11.1 ContentHandler
Das Interface ContentHandler deklariert alle Methoden, die ein SAXParser während des normalen Parsens aufruft. Das Interface deklariert die
folgenden Methoden, die beim Auftritt der passenden Ereignisse aufgerufen werden:
■ void startDocument()
Beginn des Dokuments. Diese Methode wird als erste vor allen anderen Methoden aufgerufen.
■ void endDocument()
Das Ende des Dokuments. Wird als letzte Methode nach dem
Abschluss des Parsens aufgerufen.
■ void startElement(String uri, String localName,
String qName, Attributes atts)
Beginn eines Elements. Diese Methode wird aufgerufen, wenn der
Parser mit der Verarbeitung eines Elements beginnt. Dabei bezeichnet
uri den Namensraum, zu dem das Element gehört (eventuell leerer
String), localName den eigentlichen Elementnamen, qName den vollen
Namen (namespace:localName) des Elements und atts eine Liste
der Attribute des Elements.
■ void endElement(String uri, String localName,
String qName)
Wird aufgerufen beim Ende eines Elements.
■ void characters(char[] ch, int start, int length)
Lesen von Zeichenketten. Wird aufgerufen, wenn der Parser eine Zeichenkette liest. In dem Feld ch[] wird die Zeichenkette gespeichert,
die den gesamten Inhalt des Vaterknotens enthält. start bezeichnet
Einführung in XML
214
den Anfang des Strings in diesem Feld, length die Länge der Zeichenkette.
Aus diesen drei Argumenten kann man ganz einfach ein String-Objekt
wie folgt erzeugen:
String s = new String(ch, start, length);
■ void ignorableWhitespace(char[] ch, int start,
int length)
Liefert Zeichenketten, die aus sog. Whitespaces bestehen, falls man
diese Zeichen braucht. In der Regel werden sie jedoch ignoriert.
■ void processingInstruction(String target, String data)
Auftritt einer Prozessorinstruktion. target enthält den Namen der
Prozessorinstruktion, data enthält die weiteren Daten der Prozessorinstruktion. Für die XML-Deklaration <?xml version='...'?> wird
diese Methode nicht aufgerufen, da es sich hierbei nicht um eine Prozessorinstruktion handelt.
■ void setDocumentLocator(Locator locator)
Registriert ein Objekt zur Ermittlung der Position des Parsers im
Dokument (welche Zeile, welches Zeichen). Meistens verwendet man
diese Methode, um eine lokale Referenz auf den Locator zu speichern.
■ void skippedEntity(String name)
Wenn der Parser Entities ignoriert, wird diese Methode aufgerufen.
Bei Xerces ist dies z. B. nicht der Fall.
Zusätzlich ist anzumerken, dass Xerces uns einen einfachen DefaultHandler zur Verfügung stellt, der die Methoden mit einem Standardverhalten implementiert. Wir können also diesen DefaultHandler als Basis
verwenden und nur die Methoden überschreiben, die wir mit einem neuen
Verhalten versehen wollen:
public class MyHandler extends DefaultHandler {...}
Im folgenden Beispiel geben wir die auftretenden Ereignisse eines XMLDokuments aus:
import java.io.FileReader;
import org.xml.sax.XMLReader;
import org.apache.xerces.parsers.SAXParser;
Kapitel 11 – SAX
import
import
import
import
215
org.xml.sax.Attributes;
org.xml.sax.InputSource;
org.xml.sax.helpers.XMLReaderFactory;
org.xml.sax.helpers.DefaultHandler;
public class MySAXApp extends DefaultHandler {
public MySAXApp () {
super();
}
public void startDocument () {
System.out.println("Start document");
}
public void endDocument () {
System.out.println("End document");
}
public void startElement (String uri, String name,
String qName, Attributes atts){
System.out.println("Start element: " + qName);
}
public void endElement (String uri, String name,
String qName) {
System.out.println("End element: " + qName);
}
public void characters (char ch[], int start, int length){
if (length > 0) {
System.out.print("Characters: ");
for (int i = start; i < start + length; i++) {
System.out.print(ch[i]);
}
System.out.println();
}
}
public static void main (String args[])
throws Exception {
// XMLReader xr = XMLReaderFactory.createXMLReader();
XMLReader xr = new SAXParser();
MySAXApp handler = new MySAXApp();
xr.setContentHandler(handler);
Einführung in XML
216
xr.setErrorHandler(handler);
// Parse alle übergebenen Dateien
for (int i = 0; i < args.length; i++) {
FileReader r = new FileReader(args[i]);
xr.parse(new InputSource(r));
}
}
}
Bemerkung: Wir haben einen Parser mit XMLReader xr = new SAXPardirekt erzeugt und uns auf einen bestimmten SAXParser festgelegt. In der Regel kann man durch den Aufruf von XMLReaderFactory.createXMLReader(); den verwendeten Parser vom Benutzer bzw.
durch die Systemeinstellungen bestimmen lassen.
ser();
Wir betrachten als Beispiel das folgende XML-Dokument hallo.xml:
<?xml version='1.0' standalone='yes'?>
<greetings>
<greeting lang="de">
Hallo Welt!
</greeting>
<greeting lang="en">
Hello World!
</greeting>
</greetings>
Mit dem Aufruf java MySAXApp hallo.xml erhalten wir die Ausgabe:
Start document
Start element: greetings
Characters:
Start element: greeting
Characters:
Hallo Welt!
Characters:
End element: greeting
Characters:
Start element: greeting
Characters:
Hello World!
Kapitel 11 – SAX
217
Characters:
End element: greeting
Characters:
End element: greetings
End document
Man sieht an diesem Beispiel ganz schön, dass Whitespace-Zeichen hier
zum Inhalt der Elemente gehören. Sie können als „ignorable Whitespaces“ nur erkannt werden, wenn eine DTD oder ein XML-Schema eingebunden ist.
11.2 ErrorHandler
Das Interface ErrorHandler dient zur Implementierung von CallbackMethoden für die Fehlerbehandlung. Der SAX-Parser ruft dann die
Methoden dieses Interfaces beim Eintritt der jeweiligen Fehler auf. Das
Interface deklariert die folgenden Methoden, welche die drei Fehlerarten
vom XML-Standard behandeln:
■ void warning(SAXParseException exception)
Wird aufgerufen bei Eintritt von Warnungen. Warnungen können in
den im XML-Standard genannten Fällen ausgegeben werden, zum
Beispiel wenn in der DTD Attribute für nicht deklarierte Elementtypen
angegeben werden; dies ist jedoch kein Fehler.
■ void error(SAXParseException exception)
Wird aufgerufen bei Eintritt von „normalen“ (nicht kritischen) Fehlern. Ein nicht kritischer Fehler ist eine Verletzung der Regeln der
XML-Spezifikation, wobei die Konsequenzen nicht definiert sind.
Zum Beispiel ist es ein Fehler, wenn die Validierung aktiviert ist und
das Dokument nicht der angegebenen Grammatik genügt.
■ void fatalError(SAXParseException exception)
Wird aufgerufen beim Eintritt von „kritischen“ Fehlern. Kritische Fehler sind solche, die den Parsing-Prozess aufhalten. Meistens ist dies der
Fall, wenn das Dokument nicht wohlgeformt ist.
218
Einführung in XML
Das übergebene Argument vom Typ SAXParseException enthält alle
Informationen (Zeilennummer, Fehlermeldung etc.) über den Fehler und
kann diese mit den entsprechenden Methoden zurückliefern.
In dem folgenden Beispiel geben wir einige Informationen über jeden
Fehler oder jede Warnung aus:
class MyErrorHandler implements ErrorHandler {
public void warning(SAXParseException exception)
throws SAXException {
System.out.println("**Warnung**\n" +
" Zeile: " + exception.getLineNumber() + "\n" +
" Meldung: " + exception.getMessage());
throw new SAXException("Warnung");
}
public void error(SAXParseException exception)
throws SAXException {
System.out.println("**nicht kritischer Fehler**\n" +
" Zeile: " + exception.getLineNumber() + "\n" +
" Meldung: " + exception.getMessage());
throw new SAXException("nicht kritischer Fehler");
}
public void fatalError(SAXParseException exception)
throws SAXException {
System.out.println("**kritischer Fehler**\n" +
" Zeile: " + exception.getLineNumber() + "\n" +
" Meldung: " + exception.getMessage());
throw new SAXException("kritischer Fehler");
}
}
Wir registrieren diesen ErrorHandler bei dem SAX-Parser aus dem obigen Beispiel mit:
MyErrorHandler errHandler = new MyErrorHandler();
xr.setErrorHandler(errHandler);
Um einen Fehler zu produzieren, ändern wir z. B. den Tag-Namen <greetings> in <greeting> um und bekommen die Ausgabe:
Start document
Kapitel 11 – SAX
219
Start element: greeting
Characters:
Start element: greeting
Characters:
Hallo Welt!
Characters:
End element: greeting
Characters:
Start element: greeting
Characters:
Hello World!
Characters:
End element: greeting
Characters:
**kritischer Fehler**
Zeile: 9
Meldung: The end-tag for element type "greeting" must end
with a '>' delimiter.
Exception in thread "main" org.xml.sax.SAXException:
kritischer Fehler
at MyErrorHandler.fatalError(MySAXApp.java:36)
at org.apache.xerces.util.ErrorHandlerWrapper.fatalError
at org.apache.xerces.impl.XMLErrorReporter.reportError
at org.apache.xerces.impl.XMLErrorReporter.reportError
at org.apache.xerces.impl.XMLScanner.reportFatalError
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.
scanEndElement
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl
$FragmentContentDispatcher.dispatch
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.
scanDocument
at org.apache.xerces.parsers.XML11Configuration.parse
at org.apache.xerces.parsers.XML11Configuration.parse
at org.apache.xerces.parsers.XMLParser.parse
at org.apache.xerces.parsers.AbstractSAXParser.parse
at MySAXApp.main(MySAXApp.java:88)
Einführung in XML
220
11.3 Beispiel: Counter
Das folgende Beispiel Counter hat seinen Ursprung in einem Java-Beispielprogramm zur Xerces-Bibliothek. Das Programm bekommt ein
XML-Dokument als Argument, parst dieses und gibt eventuelle Fehlermeldungen aus. Bei Erfolg werden Anzahl der Elemente, Zeichen und
Attribute sowie der Zeitaufwand ausgegeben.
Die auf einem DOM-Parser basierende Variante dieses Beispiels wird
in den Übungen unter dem Alias xmlParse zur Prüfung von XML-Dokumenten eingesetzt.
import
import
import
import
import
import
import
java.io.PrintWriter;
org.xml.sax.Attributes;
org.xml.sax.SAXException;
org.xml.sax.SAXParseException;
org.xml.sax.XMLReader;
org.xml.sax.helpers.XMLReaderFactory;
org.xml.sax.helpers.DefaultHandler;
public class Counter extends DefaultHandler {
/** Default-Parser*/
protected static final String DEFAULT_PARSER_NAME =
"org.apache.xerces.parsers.SAXParser";
protected long fElements;
protected long fAttributes;
protected long fCharacters;
protected long fIgnorableWhitespace;
protected long fTagCharacters;
/** Default constructor. */
public Counter() {} // <init>()
/** Prints the results. */
public void printResults(PrintWriter out, String uri,
long time) {
out.print(uri);
out.print(": ");
out.print(time);
out.print(" ms");
out.print(" (");
out.print(fElements);
out.print(" elems, ");
Kapitel 11 – SAX
221
out.print(fAttributes);
out.print(" attrs, ");
out.print(fIgnorableWhitespace);
out.print(" spaces, ");
out.print(fCharacters);
out.print(" chars)");
out.println();
out.flush();
}
public void startDocument() throws SAXException {
fElements
= 0;
fAttributes
= 0;
fCharacters
= 0;
fIgnorableWhitespace = 0;
fTagCharacters
= 0;
}
public void startElement(String uri, String local,
String raw, Attributes attrs)
throws SAXException {
fElements++;
fTagCharacters++; // open angle bracket
fTagCharacters += raw.length();
if (attrs != null) {
int attrCount = attrs.getLength();
fAttributes += attrCount;
for (int i = 0; i < attrCount; i++) {
fTagCharacters++; // space
fTagCharacters += attrs.getQName(i).length();
fTagCharacters++; // '='
fTagCharacters++; // open quote
fTagCharacters++; // close quote
}
}
fTagCharacters++; // close angle bracket
}
public void characters(char ch[], int start, int length)
throws SAXException {
fCharacters += length;
}
public void ignorableWhitespace(char ch[],
int start, int length)
Einführung in XML
222
throws SAXException {
fIgnorableWhitespace += length;
}
public void processingInstruction(String target,
String data)
throws SAXException {
fTagCharacters += 2; // "<?"
fTagCharacters += target.length();
if (data != null && data.length() > 0) {
fTagCharacters++; // space
}
fTagCharacters += 2; // "?>"
}
public void warning(SAXParseException ex)
throws SAXException {
printError("Warning", ex);
}
public void error(SAXParseException ex)
throws SAXException {
printError("Error", ex);
}
public void fatalError(SAXParseException ex)
throws SAXException {
printError("Fatal Error", ex);
}
protected void printError(String type,
SAXParseException ex) {
System.err.print("[");
System.err.print(type);
System.err.print("] ");
if (ex == null) {
System.out.println("!!!");
}
String systemId = ex.getSystemId();
if (systemId != null) {
int index = systemId.lastIndexOf('/');
if (index != -1)
systemId = systemId.substring(index + 1);
System.err.print(systemId);
}
Kapitel 11 – SAX
223
System.err.print(':');
System.err.print(ex.getLineNumber());
System.err.print(':');
System.err.print(ex.getColumnNumber());
System.err.print(": ");
System.err.print(ex.getMessage());
System.err.println();
System.err.flush();
}
public static void main(String argv[]) {
if (argv.length != 1) {
System.out.println("usage: Counter filename");
System.exit(1);
}
String XMLFile = argv[0];
Counter counter = new Counter();
PrintWriter out = new PrintWriter(System.out);
XMLReader parser = null;
try {
parser = XMLReaderFactory.createXMLReader(
DEFAULT_PARSER_NAME);
} catch (Exception e) {
System.err.println(
"error: Unable to instantiate parser ("+
DEFAULT_PARSER_NAME+")");
}
parser.setContentHandler(counter);
parser.setErrorHandler(counter);
try {
long timeBefore = System.currentTimeMillis();
parser.parse(XMLFile); // Parsen
long timeAfter = System.currentTimeMillis();
long time = timeAfter - timeBefore;
counter.printResults(out, XMLFile, time);
}
catch (SAXParseException e) {
// ignore
}
catch (Exception e) {
System.err.println("error: Parse error occurred - "+
e.getMessage());
Exception se = e;
if (e instanceof SAXException) {
se = ((SAXException)e).getException();
Einführung in XML
224
}
if (se != null)
se.printStackTrace(System.err);
else
e.printStackTrace(System.err);
}
}
}
Mit dem Aufruf java Counter greetings.xml bekommen wir die Ausgabe:
greetings.xml: 234 ms (3 elems, 2 attrs, 9 spaces, 45 chars)
225
12 XML-RPC und SOAP
Die auch von uns formulierte Aussage, XML entwickle sich zum universellen Datenaustauschformat, eröffnet natürlich die Frage, inwieweit
XML als Protokollsprache dienen kann. Tatsächlich gibt es mit XMLRPC (Remote Procedure Call) [45] einen XML-basierten Standard (allerdings nicht W3C unterstützt) für entfernte Prozeduraufrufe. Zu nennen
sind ferner SOAP (Simple Object Access Protocol) [4, 46] als Teil einer
größeren Web Services Initiative [44] innerhalb des W3C, die in Richtung
Middleware geht.
Grundsätzlich ist Remote Procedure Call ein synchrones Kommunikations- und Prozesssynchronisierungskonzept, bei dem das wohlbekannte
Prinzip der Prozedur- oder Funktionsaufrufe auf verteilte Umgebungen
erweitert wurde. Der Aufrufer (Auftraggeber) ruft eine Prozedur (Dienstleistung) eines anderen Rechners auf und übergibt die Aufrufparameter.
Der Aufrufer wartet dann auf die Beendigung des Unterauftrags. Bei
Beendigung durch den Dienstgeber erhält er eine Bestätigung und ggf.
Rückgabeargumente.
In realen Implementierungen, z.B. den bekannten UNIX RPC-Paketen
als Teil der Interprozesskommunikation (IPC), wirken RPCs eher unhandlich, weil die Fragen der Bekanntmachung der Dienste und Adressen, die
Probleme mit unterschiedlichen Datentypen (abhängig von den entsprechenden Wirtssprachen, häufig C) und Repräsentationen zwischen heterogenen Rechnerarchitekturen sowie die Fragen des Ausfalls eines Teilnehmers geregelt werden müssen.
226
Einführung in XML
Mit XML versucht der neue Standard1 die Schwierigkeiten von RPC
dadurch zu umgehen, dass die Nachrichten zwischen Aufrufer (Client)
und Dienstanbieter (Server), die über HTTP verbunden sind, im XMLFormat ausgetauscht werden. Das Kodieren und Dekodieren der Nachrichten kann somit von einem XML-Parser und einer XML-RPC-Schnittstelle übernommen werden.
Der XML-RPC-Standard wird von verschiedenen Unternehmen vorangetrieben, eine Beschreibung findet sich auf der Seite http://
www.xmlrpc.com.
12.1 Der Standard
Der XML-RPC-Standard basiert auf dem HTTP-Protokoll zur Übertragung von Nachrichten. Das macht die Implementierung einfacher, da die
meisten Programmiersprachen HTTP unterstützen. Zur Übertragung einer
Nachricht wird die HTTP-POST-Methode verwendet. Eine XML-RPCNachricht sieht somit wie folgt aus:
POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181
<?xml version='1.0'?>
<methodCall>
...
</methodCall>
Der erste Teil der Nachricht (bis zur leeren Zeile) ist der sog. Header einer
HTTP-Nachricht. Er enthält Informationen über die Nachricht (Art,
Datenformat, Länge etc.). Danach folgt die eigentliche Nachricht: das
XML-RPC-Dokument.
XML-RPC ist ein sehr einfaches Protokoll. Es definiert eine XMLSprache für Prozeduraufrufe und die Übermittlung von Rückgabewerten.
1. Wir verwenden hier den von Morad Ahmad eingeführten Begriff „Standard“, weisen aber nochmals darauf hin, dass nur SOAP ein W3C-Standard ist.
Kapitel 12 – XML-RPC und SOAP
227
Der Server, der eine Nachricht erhält, analysiert mit Hilfe eines XML-Parsers einen Prozeduraufruf und ermittelt die eigentliche Methode, die aufgerufen werden soll. Falls eine solche Methode existiert, wird sie ausgeführt. Mittels der XML-Bibliothek werden die Rückgabewerte in das
XML-RPC-Format gewandelt und an den Client übertragen. Auf der Clientseite läuft ein ähnlicher Vorgang ab.
12.1.1 Methodenaufrufe
Methodenaufrufe werden mit dem Element <methodCall> definiert. Das
folgende Beispiel (Name eines US-Bundesstaates für eine gegebene Staatennummer liefern) zeigt einen Methodenaufruf in XML-RPC:
<?xml version='1.0'?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>
Damit rufen wir die Methode getStateName des Handlers examples
beim Server auf.
Parameter und Datentypen
Damit Parameter zwischen verschiedenen Programmiersprachen und
Plattformen reibungslos ausgetauscht werden, legt XML-RPC die Parameterbeschreibung und die erlaubten Datentypen fest. Darüberhinaus
werden die Eigenschaften eines jeden Datentyps beschrieben. Ein Integer kann z. B. in unterschiedlichen Programmiersprachen 8, 16 oder 32
Bit lang sein.
Das Element <params> dient zur Parameterbeschreibung der
Methode. Jeder Parameter wird durch ein <param>-Element angegeben.
Jedes <param>-Element enthält ein <value>-Unterelement, das wiederum ein Unterelement besitzt, das den Datentyp des Parameters
beschreibt.
Einführung in XML
228
Mögliche Typen sind atomare Werte (zum Beispiel Zahl oder Zeichenkette), komplexe Datentypen (<struct>) und Felder (<array>). Ein
String wird angenommen, falls kein Datentyp angegeben wird. XMLRPC definiert die folgenden atomaren Datentypen:
Element
Datentyp
Beispiel
<i4> oder <int>
4-Byte-Integer mit
Vorzeichen
-12
<boolean>
0 (false) oder 1 (true)
1
<string>
String
Hallo
<double>
Double
Fließkommazahl
-12.214
<dateTime.iso8601>
Datum/Zeit
19980717T14:08:55
<base64>
Base64-Kodierung
eW91IGNhbid0IHJ=
Weiterhin werden komplexe Datentypen unterstützt. Ein <struct> hat
verschiedene <member>-Elemente, die diese Struktur beschreiben. Jedes
<member>-Element besteht aus einem <name>- und einem <value>-Element. Werte können wiederum einfache oder komplexe Datentypen oder
Felder sein. Das folgende Beispiel enthält eine Struktur mit zwei Elementen:
<struct>
<member>
<name>lowerBound</name>
<value><i4>18</i4></value>
</member>
<member>
<name>upperBound</name>
<value><i4>139</i4></value>
</member>
</struct>
Kapitel 12 – XML-RPC und SOAP
229
Ein <array> dient zur Beschreibung eines Feldes. Es enthält eine Liste
von Werten, wobei diese nicht unbedingt denselben Datentyp haben müssen. Die einzelnen Elemente werden über ihren Index angesprochen. Beispiel:
<array>
<data>
<value><i4>12</i4></value>
<value><string>Egypt</string></value>
<value><boolean>0</boolean></value>
<value><i4>-31</i4></value>
</data>
</array>
12.1.2 Rückgaben
Das Ergebnis der Ausführung einer Prozedur (oder Methode) wird in
einem XML-Dokument an den Aufrufer (den Client) zurückgeliefert. Für
die Beschreibung der Rückgabe dient das Element <methodResponse>.
Das folgende Beispiel zeigt seinen Aufbau:
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>Montana</string></value>
</param>
</params>
</methodResponse>
kann ebenfalls Fehlermeldungen enthalten. Dazu
dient das Element <fault>.
<methodResponse>
12.2 XmlRpc-Schnittstelle
Über die XmlRpc-Schnittstelle kann man einen sog. XmlRpc-Server und
XmlRpc-Client entwickeln, die miteinander XML-RPC-Dokumente austauschen. Diese Schnittstelle verbirgt die technischen Details und ermöglicht damit einem Programmierer ohne XML-Kenntnisse, XML-RPC zu
verwenden.
230
Einführung in XML
Auf der Clientseite kodiert die Schnittstelle die Aufrufe in der XMLRPC-Sprache und verschickt sie an den Server mittels des HTTP-Protokolls. Auf der Serverseite wird der XML-Text gefiltert und in die entsprechenden Methodenaufrufe umgewandelt. Die Resultate vom Server werden wiederum in XML-RPC-Dokumente umgewandelt und an den Client
übertragen, wo sie wieder durch die XmlRpc-Schnittstelle in die interne
Darstellung des Clients umgewandelt werden.
12.2.1 Installieren und Verwenden der XmlRpc-Schnittstelle
In unseren Beispielen greifen wir wieder auf die Implementierung der
Apache-Gemeinde unter ws.apache.org/xmlrpc zurück.1 Nachdem wir
das Paket heruntergeladen haben, kopieren wir die JAR-Archive in ein
beliebiges Verzeichnis und fügen sie der CLASSPATH-Variable hinzu.
Alternativ können die Archive auch in das Verzeichnis jre/lib/ext
unterhalb der Java-Installation kopiert werden. In diesem Fall ist die
Anpassung der CLASSPATH-Variable nicht nötig.
In Java-Programmen können wir dann die Klassen des Pakets mit
import org.apache.xmlrpc.*;
einbinden. Einige dieser Klassen besprechen wir in diesem Kapitel.
Die Schnittstelle stellt unter anderem die Klassen XmlRpcServer und
XmlRpcClient zur Verfügung, die zur Implementierung von Servern und
Clients dienen. Weiterhin gibt es die Klasse WebServer, die einen einfachen Webserver implementiert (HTTP-Protokoll).
Für das Parsen der XML-Dokumente (ein- und ausgehende Nachrichten) muss ein SAX-Parser spezifiziert werden. Da die Schnittstelle keinen
bestimmten festlegt, müssen wir auf Server- und Clientseite zuerst den
SAX-Parser bestimmen:
XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");
1. Wir verwenden hier die Version 2 dieser Implementierung.
Kapitel 12 – XML-RPC und SOAP
231
Serverimplementierung
Zum Erzeugen eines Servers können wir eine Instanz der Klasse WebServer erzeugen. Als Argument geben wir den Port 8081 an, auf dem der Server lauschen soll:
WebServer server = new WebServer(8081);
Als letztes können mehrere Handler registriert werden, so dass sie von
Clients verwendet werden können:
server.addHandler("hello", new HelloHandler());
Dabei bezeichnet hello den Namen, mit dem die Clients diesen Handler
ansprechen können. Zusätzlich kann man mit der Methode acceptClient
bestimmen, welche Clients Kontakt mit diesem Server aufnehmen dürfen,
worauf wir hier aber nicht im Detail eingehen. Schließlich muss der Server noch gestartet werden:
server.start();
Clientimplementierung
Die Entwicklung von Clients ist noch einfacher. Wir müssen zuerst einen
Client mit der Klasse XmlRpcClient erzeugen:
XmlRpcClient client = new
XmlRpcClient("http://localhost:8081");
Das Argument gibt den Servernamen und den Port an, auf dem der Server
läuft. Danach können wir mit der Methode execute(method, args)
Methoden bei dem entfernten Server ausführen. Die Rückgabewerte von
execute sind die gelieferten Werte der Servermethoden.
12.3 Beispiel
In diesem Beispiel entwickeln wir einen einfachen XmlRpc-Server und
-Client. Der Server bietet die einzige Methode sayHello, die von dem
Handler hello implementiert wird. Der Client ruft diese Methode beim
Starten mit dem ersten Programmargument auf.
Einführung in XML
232
12.3.1 HelloServer
import org.apache.xmlrpc.WebServer;
import org.apache.xmlrpc.XmlRpc;
import org.apache.xerces.parsers.SAXParser;
public class HelloServer {
public static void main(String args[]) {
try {
XmlRpc.setDriver(
"org.apache.xerces.parsers.SAXParser");
// Erzeuge Server
WebServer server = new WebServer(8081);
// Registrieren Handler-Klassen
server.addHandler("hello", new HelloHandler());
System.out.println("Warte auf Anfragen ...");
server.start();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
12.3.2 HelloHandler
public class HelloHandler {
public String sayHello(String str) {
return "Hello " + str;
}
}
12.3.3 HelloClient
import
import
import
import
java.util.Vector;
org.apache.xmlrpc.XmlRpc;
org.apache.xmlrpc.XmlRpcClient;
org.apache.xerces.parsers.SAXParser;
public class HelloClient {
public static void main(String args[]) {
if (args.length != 1) {
Kapitel 12 – XML-RPC und SOAP
233
System.out.println("usage: java HelloClient name");
System.exit(1);
}
try {
XmlRpc.setDriver(
"org.apache.xerces.parsers.SAXParser");
XmlRpcClient client =
new XmlRpcClient("http://localhost:8081");
Vector params = new Vector();
params.addElement(args[0]);
String result = (String)
client.execute("hello.sayHello", params);
System.out.println("Antwort vom Server: " + result);
}
catch(Exception ex) {
ex.printStackTrace();
}
}
}
Wir kompilieren die beiden Programme mit:
javac HelloServer.java
javac HelloClient.java
Server starten:
>java HelloServer &
Warte auf Anfragen ...
Client aufrufen:
>java HelloClient XML
Antwort vom Server: Hello XML
12.4 SOAP
SOAP ist die W3C-Version des Remote Procedure Calls. Nach Ansicht
einiger der ehemaligen XML-RPC-Mitglieder enthält SOAP einige unnötig aufgeblähte Elemente mit seltsamen Features, andererseits scheint sich
SOAP durchzusetzen.
SOAP, derzeit in Version 1.2 als W3C-Recommendation [46] verfügbar, unterstützt XML Schema, eine Kombination von structs und arrays
Einführung in XML
234
sowie benutzerdefinierte Typen. SOAP erlaubt keine DTDs. SOAP implementiert ein zustandsloses Nachrichtenaustausch-Protokoll.
12.4.1 Botschaftenstruktur
Eine SOAP-Botschaft ist ein gewöhnliches XML-Dokument mit den folgenden Elementen:
• ein <Envelope>-Element, welches das XML-Dokument als
SOAP-Botschaft kennzeichnet (Umschlag, Wurzelelement).
• ein optionales <Header>-Element mit Header-Information, spez.
für Transaktionen und Authentifizierung.
• ein <Body>-Element mit Aufruf- und Antwort-Informationen.
• ein optionales <Fault>-Element, das Informationen zu Fehlern
enthält, die möglicherweise beim Nachrichtenaustausch aufgetreten sind.
Alle diese Elemente sind im Standardnamensraum für den SOAP envelope vereinbart:
http://www.w3.org/2003/05/soap-envelope
Der optionale Namensraum für die Kodierungen und Datentypen ist
http://www.w3.org/2003/05/soap-encoding
Für Fehlermeldungen bei Prozeduraufrufen wird der folgende Namensraum eingesetzt:
http://www.w3.org/2003/05/soap-rpc
Der grundsätzliche Aufbau ist demnach:
<?xml version="1.0"?>
<env:Envelope
xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Header>
...
</env:Header>
<env:Body>
...
Kapitel 12 – XML-RPC und SOAP
235
<env:Fault>
...
</env:Fault>
...
</env:Body>
</enc:Envelope>
12.4.2 Der SOAP-Kopf (Header)
Die Kindelemente des <Header>-Elements können
• ein encodingStyle-Attribut
• ein role-Attribut mit den Werten
http://www.w3.org/2003/05/soap-envelope/role/next
http://www.w3.org/2003/05/soap-envelope/role/none
http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver
• ein mustUnderstand-Attribut mit den Werten true oder false
• ein relay-Attribut, auch mit true oder false
enthalten. Es folgt ein Beispiel mit zwei Elementen im Header.
<env:Envelope
xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Header>
<n:ext xmlns:n="http://example.org/ext"
env:mustUnderstand="true">
<n:someint>1</n:someint>
</n:ext>
<m:ext2 xmlns:m="http://example.org/ext2"
env:role
="http://www.w3.org/2003/05/soap-envelope/role/next"
env:mustUnderstand="true">
<m:someint>1</m:someint>
</m:ext2>
</env:Header>
<env:Body>
<my:message xmlns:my="http://www.mycompany.com/myns">
<my:text>Hello World!</my:text>
</my:message>
</env:Body>
</env:Envelope>
236
Einführung in XML
12.4.3 Der SOAP-Rumpf (Body)
Der Body einer SOAP-Nachricht enthält die anwendungsspezifischen
Anfragen, Übergabewerte und eventuelle Fehlermeldungen. Es wird empfohlen, dass das env:Body-Element mindestens ein Unterelement enthält
und einen eigenen Namensraum eröffnet.
Die folgende Abfrage ermittelt einen Preis.
<?xml version="1.0"?>
<env:Envelope
xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<m:GetPrice xmlns:m="http://www.w3schools.com/prices">
<m:Item>Apples</m:Item>
</m:GetPrice>
</env:Body>
</env:Envelope>
Eine mögliche Antwort könnte wie folgt aussehen.
<?xml version="1.0"?>
<env:Envelope
xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<m:GetPriceResponse
xmlns:m="http://www.w3schools.com/prices">
<m:Price>1.90</m:Price>
</m:GetPriceResponse>
</env:Body>
</env:Envelope>
12.4.4 Fehlerfälle
Das Body-Element kann ein Fault-Element enthalten, das über einen aufgetretenen Fehler Aufschluss gibt. Der Fehler kann beim Aufrufer entstanden sein (falsche Parameter, kein gültiges XML, usw.) oder der Empfänger des Aufrufs kann den RPC nicht verarbeiten, obwohl dieser gültig
ist.
<?xml version='1.0'?>
<env:Envelope
xmlns:env="http://www.w3.org/2003/05/soap-envelope"
Kapitel 12 – XML-RPC und SOAP
237
xmlns:rpc="http://www.w3.org/2003/05/soap-rpc">
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:Sender</env:Value>
<env:Subcode>
<env:Value>rpc:BadArguments</env:Value>
</env:Subcode>
</env:Code>
<env:Reason>
<env:Text xml:lang="en-US">Processing error</env:Text>
<env:Text xml:lang="cs">Chyba zpracování</env:Text>
</env:Reason>
<env:Detail>
<e:myFaultDetails
xmlns:e="http://travelcompany.example.org/faults">
<e:message>Name does not match card number</e:message>
<e:errorcode>999</e:errorcode>
</e:myFaultDetails>
</env:Detail>
</env:Fault>
</env:Body>
</env:Envelope>
Das env:Fault-Element enthält immer zwei vorgeschriebene Unterelemente, env:Code und env:Reason, sowie optional anwendungsspezifische Informationen in einem env:Detail-Unterelement.
Das env:Code-Unterelement in env:Fault hat selbst wieder ein
Pflichtelement, env:Value, dessen Inhalt in der SOAP-Spezifikation
(siehe SOAP Part 1 section 5.4.6) angegeben ist. Im Beispiel oben wird
mit „env:Sender“ der Aufrufer als Fehlerverursacher angegeben.
Zugleich enthält das Beispiel (aus dem SOAP Primer) ein env:Subcode, der angibt, dass ein RPC-spezifischer Fehler, hier rpc:BadArguments, definiert in SOAP Part 2 section 4.4, der Grund für die Nichtbearbeitung des Kundenwunsches ist.
12.4.5 Übertragungsprotokolle
Prinzipiell ist SOAP mit vielen Übertragungsprotokollen kombinierbar.
Das Standardisierungsdokument zeigt die Nutzung im Rahmen von HTTP
238
Einführung in XML
GET und POST, nennt aber auch eine mögliche Nutzung über E-Mail mit
SMTP. Auf die Details verzichten wir hier.
239
Literaturverzeichnis
[1]
W3C: Extensible Markup Language (XML) 1.0 (Fifth Edition). W3C Recommendation, 26. November 2008, URL:
http://www.w3.org/TR/2008/REC-xml-20081126/
[2]
W3C: Scalable Vector Graphics (SVG) 1.1 (Second Edition). W3C Recommendation, 16. August 2011, URL:
http://www.w3.org/TR/2011/REC-SVG11-20110816/
[3]
W3C: W3C Math Home.
URL: http://www.w3.org/Math
[4]
W3C: Simple Object Access Protocol (SOAP).
URL: http://www.w3.org/TR/SOAP
[5]
SNELL, JAMES, DOUG TIDWELL, and PAVEL KULCHENKO:
Programming Web Services with SOAP. O'Reilly & Associates, Inc., 2002.
[6]
WINER, DAVE: XML-RPC Specification. URL: http://
www.xmlrpc.com/spec, 1999.
[7]
RAY, ERIK T.: Einführung in XML. O'Reilly & Associates,
Inc., 2001.
[8]
W3C: XML Schema Part 0: Primer (Second Edition).
W3C Recommendation, 28. Oktober 2004, URL: http://
www.w3.org/TR/2004/REC-xmlschema-0-20041028/
[9]
W3C: The Extensible Stylesheet Language (XSL).
URL: http://www.w3.org/Style/XSL.
Einführung in XML
240
[10]
W3C: XSL Transformations (XSLT) Version 1.0. W3C Recommendation, 16. November 1999, URL: http://
www.w3.org/TR/1999/REC-xslt-19991116
[11]
TIDWELL, DOUG: XML-Dokumente transformieren mit XSLT. O'Reilly & Associates, Inc., 2003.
[12]
W3C: XML Path Language (XPath) Version 1.0. W3C Recommendation, 16. November 1999, URL: http://
www.w3.org/TR/1999/REC-xpath-19991116/
[13]
W3C: XML Pointer Language (XPointer). W3C Working
Draft, 16. August 2002, URL: http://www.w3.org/TR/
2002/WD-xptr-20020816/
[14]
W3C: XML Linking Language (XLink) Version 1.1. W3C
Recommendation, 6. Mai 2010, URL: http://
www.w3.org/TR/2010/REC-xlink11-20100506/
[15]
W3C: Document Object Model (DOM) Level 1 Specification (Second Edition) Version 1.0. W3C Working Draft, 29.
September 2000, URL: http://www.w3.org/TR/2000/
WD-DOM-Level-1-20000929/
[16]
W3C: Document Object Model (DOM) Level 2 Core Specification Version 1.0. W3C Recommendation, 13. November 2000, URL: http://www.w3.org/TR/2000/RECDOM-Level-2-Core-20001113/
[17]
W3C: Document Object Model (DOM) Level 3 Core Specification Version 1.0. W3C Recommendation, 7. April
2004, URL: http://www.w3.org/TR/DOM-Level-3Core/
[18]
XT. URL: http://www.blnz.com/xt/index.html.
[19]
xerces. URL: http://xml.apache.org/xerces2-j/ index.html.
[20]
xalan. URL: http://xml.apache.org/xalan-j/ index.html.
241
[21]
ECKSTEIN, ROBERT: XML Pocket Reference. O'Reilly &
Associates, Inc., 1999.
[22]
W3C: XML Schema Part 1: Structures (Second Edition).
W3C Recommendation, 28. Oktober 2004, URL: http://
www.w3.org/TR/2004/REC-xmlschema-1-20041028/
[23]
W3C: XML Schema Part 2: Datatypes (Second Edition).
W3C Recommendation, 28. Oktober 2004, URL: http://
www.w3.org/TR/2004/REC-xmlschema-2-20041028/
[24]
VLIST, ERIK VAN DER: XML Schema. O'Reilly & Associates, Inc., 2002.
[25]
W3C: XQuery 1.0: An XML Query Language. W3C Recommendation, 23. Januar 2007, URL: http://
www.w3.org/TR/2007/REC-xquery-20070123/
[26]
W3C: XML Path Language (XPath) 2.0 (Second Edition).
W3C Recommendation, 14. Dezember 2010, URL:
http://www.w3.org/TR/2010/REC-xpath2020101214/
[27]
W3C: XQuery 1.0 and XPath 2.0 Functions and Operators
(Second Edition). W3C Recommendation, 14. Dezember
2010, URL: http://www.w3.org/TR/2010/REC-xpathfunctions-20101214/
[28]
HOMER, ALEX: XML IE5 Programmer’s Reference. Wrox
Press Ltd, Birmingham (UK), 1999
[29]
WOLFGANG LEHNER und HARALD SCHÖNING: XQuery ein Überblick, Datenbank-Spektrum 11/2004
[30]
WOLFGANG LEHNER und HARALD SCHÖNING: XQuery Grundlagen und fortgeschrittene Methoden, dpunkt-Verlag, Heidelberg, 2004
[31]
HARALD SCHÖNING: XML und Datenbanken, Tutorium 2
der Datenbank-Tutorientage 2001, Deutsche InformatikAkademie, Bonn.
Einführung in XML
242
[32]
ANDREW EISENBERG and JIM MELTON: Advancements in
SQL/XML, ACM SIGMOD Record, Vol. 33, No. 3, September 2004, pp. 79-86
[33]
K. KÜSPERT, G. SAAKE und L. WEGNER: Duplicate Detection and Deletion in the Extended NF2 Data Model, Proc.
Third Int. Conference on Foundations of Data Organization and Algorithms, Paris, Juni 1989, W.Litwin and
H.J.Schek (Eds), Springer LNCS 367 (1989) 83-100
[34]
G. SAAKE, V. LINNEMANN, P. PISTOR und L. WEGNER:
Sorting, Grouping, and Duplicate Elimination in the
Advanced Information Management Prototype, Proc. 15th
Int. Conference on Very Large Data Bases, Amsterdam
(Aug. 1989) 307-316
[35]
J. E. FUNDERBURK, S. MALAIKA, and B. REINWALD: XML
programming with SQL/XML and XQuery, IBM SYSTEMS JOURNAL, Vol. 14, No. 4 (2002) pp. 642-665
[36]
CAN TÜRKER: SQL:1999 & SQL:2003 - Objektrelationales
SQL, SQLJ & SQL/XML, dpunkt.verlag, 2003
[37]
ISO/IEC 9075-14:2003 Information Technology - Database Languages - SQL - Part 14: XML-Related Specifications (SQL/XML)
[38]
Tutorial: XLink Basics, http://www.melonfire.com/
community/columns/trog/article.php?id=90
[39]
Tutorial zu XPointer, http://www.zvon.org/xxl/
XPointerTutorial/Output/index.html
[40]
W3C: XPointer element() Scheme. W3C Recommendation, 25. März 2003, URL: http://www.w3.org/TR/2003/
REC-xptr-element-20030325/
[41]
W3C: XML Protocol Working Group,
http://www.w3.org/2000/xp/Group/
[42]
DAVID BROWNEL: SAX2, O’Reilly, 1st Edition 2002
243
[43]
About SAX: Simple API for XML,
http://sax.sourceforge.net/
[44]
W3C: Web Services Activity,
http://www.w3c.org/2002/ws/
[45]
XML-RPC Home page,
http://www.xmlrpc.com/
[46]
W3C: SOAP Version 1.2 Part 0: Primer (Second Edition).
W3C Recommendation, 27. April 2007, URL: http://
www.w3.org/TR/2007/REC-soap12-part0-20070427/
[47]
ANDREW EISENBERG und JIM MELTON: XQuery 1.0 is Nearing Completion, ACM SIGMOD Record, Vol. 34, No. 4
(Dec. 2005) 78-84
[48]
JIM MELTON und STEPHEN BUXTON: Querying XML XQuery, XPath, and SQL/XML in Context, Morgan Kaufmann 2006, 814 S.
[49]
LUTZ WEGNER: Quicksort for Equal Keys, IEEE Trans. on
Computers, Vol. 34, No. 4 (April 1985) 362-367
[50]
JONATHAN ROBIE. XQuery: A Guided Tour, unter
http://www.datadirect.com/developer/xquery/
xquerybook/index.ssp.
Herunterladen