Warum Schemata
Anders als DTDs werden
Schemata in einem XML-Format definiert. Das ist an und
für sich schonmal ein Vorteil, dazu kommt aber dass das
u.A. so ist weil Schemata erst 2001 vom W3C verabschiedet worden
sind und es deshalb erheblich mehr Werkzeuge gibt die auf Konformität
mit einem DTD prüfen und Schemata noch nicht interpretieren.
Ich persönlich nutze Schemata nur dort, wo ich deren Vorteil brauche
und der ist: Daten und nicht Dokumentorientiert zu arbeiten.
Es gibt in Schemata verschiedene Datentypen, in DTDs
gibt es diese nicht. Ich kann also sagen ein Tag darf nur ein Datum
enthalten, bei DTDs muss ich damit leben dass die XML-Autoren
in ein Feld namens Datum 1999, 1.10.1999, erster Oktober 1999 oder
Mickey Mouse hat einen Bruder reinschreibt. Werden die XMLs
automatisch verarbeitet, z.B. in eine Datenbank eingetragen wird
das entsprechende Programm spätestens beim Mickey-Mouse-Satz seine
helle Freude haben.
Einbindung
Schemata werden in die XML-Datei eingebunden mit
<ROOTELEMENT xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance" xsi:noNamespaceSchemaLocation="SCHEMADATEINAME.xsd">
...
</ROOTELEMENT>
Tags
Tags werden mit dem xsd:element-Tag beschrieben.
Ein sehr einfaches Schema wäre z.B.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema" elementFormDefault="qualified">
<xsd:element name="p" type="xsd:string"/>
</xsd:schema>
Ein leeres Root-Tag habe ich in der Praxis übrigens noch nie
gesehen...
Der Typ ist übrigens mein XML-Lieblingstag: erstaunlich leicht
zu programmieren lässt sich nämlich ein eigener Typ:
Untertags
mit
<xsd:complexType name="wasbeliebt">
<xsd:sequence>
<xsd:element name="b" type="xsd:string"/>
<xsd:element name="i" type="xsd:string"/>
<xsd:element name="u" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
definieren Sie den Typ "wasbeliebt", den Sie in p mit
<xsd:element name="p" type="wasbeliebt"/>
einbinden. Er verpflichtet Sie zu einen b, einem i und einem
u-Tag mit jeweils einem String als Inhalt.
Inline Schemata
Man kann die complexTypes übrigens auch gleich ins Element einbetten:
<xsd:element name="p">
<xsd:complexType name="wasbeliebt">
<xsd:element name="i" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
Wobei sich der complexType zwar mit einem Namen bezeichnen,
aer nicht mehr ausserhalb des element-Tags als type="wasbeliebt"
angeben lässt. Dass sicht kein element-type definieren UND
gleichzeitig ein complexType einbetten lässt sollte sich von selbst
verstehen.
Tags zur Auswahl stellen
Wenn Sie den DTD-Teil
dieses Tutorials schon gelesen haben, wissen Sie aber, dass das
nicht Sinn der Sache war. Es sollen beliebig viele und geschachtelte
i us und bs möglich sein. Ein xsd:choice sieht zwar wie eine
Lösung aus, ist aber keine
<xsd:complexType name="wasbeliebt">
<xsd:choice>
<xsd:element name="b" type="xsd:string"/>
<xsd:element name="i" type="xsd:string"/>
<xsd:element name="u" type="xsd:string"/>
</xsd:choice>
</xsd:complexType>
weil es nur ein Tag erlaubt - immerhin kann das jetzt ENTWEDER b, u oder p sein.
Die Lösung liegt in den Attributen:
minOccurs
minOccurs und maxOccurs liegen in engem Wettstreit
mit type um meine persönlichen XML-Lieblingsattribute. Während Type
eine beinahe schon an PHP erinnernde intuitive (weil logische)
Programmierung erlaubt sind minOccurs und maxOccurs in vielen Tags
erlaubt und -- bedeuten immer das Gleiche (keine Ausnahmen, kein
fehlersuchen, kein Referenzenwühlen).
minOccurs="n" bedeutet: Das in dem Tag spezifizierte Element muss
mindestens n-mal vorkommen.
maxOccurs
maxOccurs="n" verhält sich analog: Das in dem Tag
spezifizierte Element darf höchstens
n-mal vorkommen. Wobei jede Zahl für eine Zahl steht und
"unbounded" für unendlich.
Mit diesen beiden Elementen lässt sich dann auch unser ibu
(italic/bold/underline)-Beispiel weiter bringen:
durch ein ergänzen einer Zeile erhält man nämlich
<xsd:choice minOccurs="0" maxOccurs="unbounded">
... die Möglichkeit, die Sequenz dreimal hintereinander zu schreiben.
An sich hätte man auch die Möglichkeit, das minOccurs und maxOccurs
hinter die einzelnen Elemente zu schreiben, etwa
<xsd:element name="b" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="i" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="u" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
so, (diese 3 Zeilen würden übrigens auch in einer Sequenz das gewünschte Ergebnis bringen).
Genaugenommen besteht ein ziemlich komplizierter Unterschied der
sich ungefähr so erklären lässt:
Entweder, man macht beliebig viele Choices die entweder aus i, b oder u
bestehen oder man macht eine Choice die aus beliebig vielen i, bs und us
besteht.
minOccurs="0" und maxOccurs="0" bei jedem Element machen die Sequenz übrigens de-facto
zu einem Choice, das Problem ist, wenn die Sequenz nicht eingehalten wird (z.B. ein u vor
einem b kommt), wird das Stylesheet invalid wenn nicht auch die Sequenz selbst
als minOccurs="0" und maxOccurs="0" definiert wurde (denn dann könnte eine Sequenzdie
nur aus u besteht vor einer Sequenz, die nur aus b besteht, existieren).
Lange Rede kurzer Sinn: obwohl es noch mindestens zwei andere Arten gibt definiert
<xsd:element name="p" type="wasbeliebt"/>
<xsd:complexType name="wasbeliebt">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="b" type="xsd:string"/>
<xsd:element name="i" type="xsd:string"/>
<xsd:element name="u" type="xsd:string"/>
</xsd:choice>
</xsd:complexType>
die Sache. Basta.
Tags in beliebiger Reihenfolge erlauben
Leichter als in DTDs kann man in Schemata Tags erlauben und
von der Reihenfolge absehen: wie xsd:sequence und xsd:choice
wird xsd:all definiert.
<xsd:element name="p" type="wasbeliebt"/>
<xsd:complexType name="wasbeliebt">
<xsd:all>
<xsd:element name="b" type="xsd:string"/>
<xsd:element name="i" type="xsd:string"/>
<xsd:element name="u" type="xsd:string"/>
</xsd:all>
</xsd:complexType>
Es arbeitet wie eine Sequence unabhängig von der Reihenfolge, mit
jeweils maximal einem Auftreten eines Tags.
Attribute
Attribute werden in einem complexType mit xsd:attribute definiert
(und dürfen im complexType immer erst NACH choices oder sequences
definiert werden).
<xsd:element name="p" type="wasbeliebt"/>
<xsd:complexType name="wasbeliebt">
<xsd:attribute name="ibu" type="xsd:string"/>
</xsd:complexType>
oder
<xsd:element name="p" type="wasbeliebt"/>
<xsd:complexType name="wasbeliebt">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="b" type="xsd:string"/>
<xsd:element name="i" type="xsd:string"/>
<xsd:element name="u" type="xsd:string"/>
</xsd:choice>
<xsd:attribute name="ibu" type="xsd:string"/>
</xsd:complexType>
(als schema2.xml bzw.
schema2.xsd)
Da haben wir übrigens den Dokumenttyp, der nur das
Top-Level-Tag beinhalten darf.
Mit dem Attribut use können Sie übrigens angeben ob das
entsprechende Attribut definiert werden muss.
Unterschieden wird zwischen "required" (muss angegeben sein),
"optional" (kann angegeben sein), "default" (kann angegeben sein)
und "prohibited" (darf nicht angegeben sein, was darauf hindeutet,
dass in Schemata irgendwo ein "if" definiert sein muss).
Ein min/maxOccurs verbietet sich deshalb, weil Attribute ja
sowieso nur höchstens einmal den gleichen
Namen haben dürfen.
default-Werte für Attribute gibt es in Schemas auch,
soweit ich das verstanden habe werden sie mit
use="default" und einem weiteren Attribut, value="Wert"
gebildet. Das habe ich auch in schema3.xsd angedeutet und
mit XMLSpy validieren lassen, aber mein Saxon 6.2.2 scheint
es nicht auszuwerten. Genausowenig wie default-Werte für Tags
nebenbei, die mit dem Attribut "default="Defaultwert"" möglich sein
sollten.
Attributaufzählungen
Sie können wie in DTDs die Auswahlmöglichkeiten bei einem
Attribut gezwungenermaßen einschränken -- und zwar durch
verwenden eines simpleTypes mit einer restriction:
<xsd:attribute name="bio" use="required">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="clear"/>
<xsd:enumeration value="investigating"/>
<xsd:enumeration value="suspicious"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
Wie man sieht ist "base" der Datentyp, der eingeschränkt werden soll.
Wertebereich einschränken
angenommen wir möchten zu p noch ein Attribut, Note.
Diese Note soll eine ganze Zahl im Bereich von 1-6 sein
(in der Schweiz von 6-1 ;)). Wir machen auch das mit einer
restriction, geben aber ein minInclusive und ein maxInclusive
vor.
<xsd:simpleType name="notentyp">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="1"/>
<xsd:maxInclusive value="6"/>
</xsd:restriction>
</xsd:simpleType>
Datentypen
Von Hause aus bieten Schemas die folgenden Datentypen:
Name
|
Beispiele
|
Erklärung
|
String
|
"Hallo Welt"
|
Zeichenkette
|
boolean
|
{true, false}
|
entweder true oder false
|
decimal
|
42.9
|
Kommazahl
|
float
|
42.9, -12.4, 44, 2.03E5, 0, -0, INF, -INF, NAN
|
INF=Unendlich, -INF=minus Unendlich, NAN=Not A Number, keine Zahl
|
duration
|
P2Y5M4DT5H17M33.4S
|
Anzahl Jahre, Anzahl Monate, Anzahl Tage, Trenner zwischen Datum und Zeit ("T"), Stunden, Minuten und Sekunden
|
dateTime
|
2001-06-08T18-46-02
|
Jahre, Monate, Tage, Trenner zwischen Datum und Zeit ("T"), Stunden, Minuten und Sekunden
|
time
|
19:12:00.03
|
Stunden,Minuten,Sekunden.Hundertstel
|
date
|
2001-06-08
|
Jahr-Monat-Tag
|
gYearMonth
|
2001-06
|
Jahre, Monate
|
gYear
|
2001
|
Jahre
|
gMonthDay
|
12-12
|
Monat, Tag
|
gDay
|
12
|
Tag
|
gMonth
|
5
|
Monat
|
hexBinary
|
12EAB0
|
Ein hexadezimaler Wert
|
anyURI
|
http://www.usegroup.de/
|
Eine URI
|
Abgeleitete Datentypen
|
integer
|
12
|
Ganzzahlen
|
positiveInteger
|
12
|
positive Ganzzahlen
|
negativeInteger
|
-12
|
Ganzzahlen kleiner Null
|
long
|
122
|
-9223372036854775808 bis 9223372036854775808
64-Bit Integer
|
int
|
122
|
-2147483648 bis 2147483647
32-Bit Integer
|
byte
|
122
|
-127 bis 128
8-Bit Integer
|
Eigene Datentypen definieren
Mit
<xsd:element name="plz" type="plz"/>
...
<xsd:simpleType name="plz">
<xsd:restriction base="xsd:integer">
<xsd:length value="5"/>
können Sie angeblich (XMLSpy interpretier offensichtlich falsch)
den Taginhalt auf 5 Zahlen beschränken,
spätestens bei dem Versuch, eine Telefonnummer mit
Vorwahl zu verlangen, muss ich mangels Testumgebung aufgeben.
Versuchen wollte ich
<xsd:restriction base="xsd:integer">
<xsd:pattern value="\d{5}\/d{5}"/>
</xsd:restriction>
für ein Pattern aus 5 digits, einem /, den man bei
regulären Ausdrücken mit dem \ maskiert (\/) und dann
nochmals 5 digits. Genaugenommen ist das eher ein
Beispiel für schlechte reguläre Ausdrücke; denn
vierstellige Vorwahlen sollten im heutigen Handyzeitalter
möglich sein und die Länge der Nummer auf keinen Fall nur
5 betragen dürfen.
Entities
"Since XML Schema begins work on the infoset of a well-formed
(perhaps valid) XML document, parsed entity definition substitution
has already taken place. We decided a two-pass architecture was too
clunky, and arguably impossible, and concluded the best thing was to
leave XML 1.0 entities to XML 1.0 mechanisms (i.e. you can define
entities in the internal subset) ", Henry S. Thompson.
Auf Deutsch: Da Schemata nur auf XML-Dateien anzuwenden sind,
die schon well-formed sind, muss jeder sehen wo er bleibt.
Zum Beispiel hier:
<!DOCTYPE ROOTELEMENT [
<!ENTITY eacute "é">
]>
in jeder XML-Datei macht zwar keinen Spaß, funktioniert aber.
Dahingegen muss ich leider zugeben, dass ich das W3C-Beispiel
für Entityähnliche Elemente nicht zum laufen bekommen habe:
We can achieve a similar but not identical outcome by declaring an element in a schema, and by setting the element's content appropriately:
<xsd:element name="eacute" type="xsd:token" fixed="é"/>
And this element can be used in an instance document:
Using an element instead of an entity in an instance document.
<?xml version="1.0" ?>
<purchaseOrder xmlns="http://www.example.com/PO1"
xmlns:c="http://www.example.com/characterElements"
orderDate="1999-10-20>
<!-- etc. -->
<city>Montr<c:eacute/>al</city>
<!-- etc. -->
</purchaseOrder>