In diesem Teil unseres XML Tutorials wollen wir uns dem Thema XML-Schema widmen, das erst nach der DTD eingeführt wurde, diese aber dennoch an zahlreichen Stellen verdrängt hat. Im Gegensatz zu DTDs kann XML Schema Single-Type-Grammatiken beschreiben. Zum Einsatz kommt dabei keine eigene Syntax, sondern die schon bekannte XML-Syntax.
XML Schema wird also in XML ausgedrückt und besitzt damit auch viele Vorteile von XML. So ist es selbstbeschreibend, zu anderen Spezifikationen kompatibel und kann zum einen von Menschen problemlos gelesen werden und zum anderen aber auch von XML-Parser ideal verarbeitet und verstanden werden. Außerdem können Namensräume berücksichtigt werden. Alles weitere zu XML Schema findet man nachfolgend. Gestartet wird mit den schon erwähnten Namensräumen.
Inhalt
Namensräume
Namensräume erlauben Elemente und Attribute eindeutig zu benennen. Wichtig ist dies beispielsweise, wenn sich Elemente oder Attribute aus unterschiedlichen Namensräume in die Quere kommen. Relativ schnell kann das beispielsweise bei der Vereinigung von zwei Dokumenten passieren. Das Element p kann beispielsweise einmal als HTML-Element (paragraph – Absatz) fungieren, zum anderen aber auch für eine „Person“ stehen. Nur durch eine Zuordnung zum richtigen Namensraum ist klar, wie p nun zu interpretieren ist. Eine solche eindeutige Zuordnung funktioniert über eine URI (Uniform Resource Identifier – beispielsweise eine URL oder URN). Diese URI dient aber nicht (!) als Quelle für irgendwelche Informationen und muss dementsprechend auch nicht auf tatsächliche Informationen verweisen (es muss dahinter kein Dokument o.ä. existieren). Sie dient lediglich als eindeutige Identifizierung und genau für eine eindeutige Identifizierung sind URIs ursprünglich konzipiert worden. Innerhalb eines Namensraums muss jeder verwendete Name aber weiterhin eindeutig sein. Dafür kann er in unterschiedlichen Namensräumen öfters benutzt werden.
Ein Namensraum folgt der folgenden Form: xmlns:Präfix=“URI“
Namensräume in XML-Schema
Was hat diese ausführliche Vorstellung aber nun mit XML-Schema zu tun? Ganz einfach, es gibt natürlich auch einen Namensraum für XML-Schema. Dort hat jedes Element im Schema das Präfix xs und ist assoziiert mit:
xmlns:xs=“http://www.w3.org/2001/XMLSchema“
Alternativ kann man auch den Präfix xsd verwenden.
Globale Definitionen kann man über das Attribut targetNamespace beeinflussen, dass man optional in das Element einfügen kann. Mit den beiden Schema-Attributen elementFormDefault und attributeFormDefault kann man hingegen global festlegen, wie mit lokalen Elementen und Attributen umgegangen werden soll. Setzt man diese Attribute jeweils auf unqualified, dann gehören die lokalen Elemente und Attribute nicht zu dem mit targetNamespace deklarierten Namensraum. Setzt man das Attribut hingegen auf „qualified“, dann gehören die Attribute und/oder Elemente auch zum Zielnamensraum des Schemas. In solch einem Fall müssen auch alle lokale Attribute und Elemente durch den Präfix des Namensraums ergänzt werden. Neben einer globalen Definition kann man die Qualifizierung der Namen auch jederzeit für einzelne Elemente und Attribute durchführen. Dafür ergänzt man diese mit dem form-Attribut.
<attribute name="Bezeichnung" type="string" form="qualified" />
Aufbau eines XML-Schemas
Damit ergibt sich folgender Aufbau eines XML-Schemas:
<?xml version="1.0" encoding="UTF-8" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- Hier stehen nun die Definitionen und Deklarationen des XML-Schemas --> </xs:schema>
Typen in XML Schema
Im Vergleich zur DTD gibt es in XML viele verschiedene vordefinierte, aber auch eigene neue Typen. Über die existierenden Datentypen können einfache und komplexe Typen definiert werden. Diese können durch Integritätsbedingungen und Kardinalitäten ergänzt werden. So kann man mit XML Schema eigene Datentypen erschaffen, die die genauen Anforderungen erfüllen.
Einfache Typen: xs:simpletype
Wie der Name schon sagt, sind einfache Datentypen einfach 😉 Konkret bedeutet dies, dass das einfache Datentypen sind, bei denen lediglich eine Einschränkung, Listenbildung oder Vereinigung möglich ist. Im Gegensatz zu komplexen Datentypen können Elemente die als einfache Datentypen definiert sind, keine andere Elemente beinhalten. Sie sind also sehr einfach strukturiert. Der Wert von Attributen gehört immer zum Datentyp simpleType, sodass Attribute keine Kindelemente oder Unterattribute enthalten können. In einer Baumstruktur sind die Blätter des Baums immer einfache Datentypen.
Elementdeklaration
Für die einfache Elementdeklaration reicht schon ein Name und ein Type aus, beispielsweise:
<element name="tutorialbezeichnung" type="xs:string" />
Es handelt sich hier also um ein Element mit der Bezeichnung zu einem Tutorial, wobei der Type ist, es sich also um eine Zeichenkette handelt (Text). Durch weitere Attribute kann man die Elementdeklaration noch detaillierter gestalten. Unter anderem kann man folgende Attribute bei der Elementdeklaration festlegen:
- default: Standardwert für Elemente
- id: Eindeutiger Identifier
- minOccurs: So häufig soll das Element mindestens vorkommen (Falls nicht gesetzt, standardmäßig 1)
- maxOccurs: So häufig soll das Element maximal vorkommen (Falls nicht gesetzt, standardmäßig 1)
- name: Name des Elements
- nillable: Erlaubt explizites „NULL“-Setzen eines Elements. Standardmäßig ist es auf false gesetzt
- ref: referenziert ein bestehendes Element, welches schon an anderer Stelle im Schema deklariert wurde
Inhaltseinschränkungen
Wenn die Elemente und Attribute definiert worden sind, kann man darüber hinaus ihre Inhalte weiter einschränken. So kann man beispielsweise die Länge des Inhalts beschränken oder durch reguläre Ausdrücke genau vorgeben, wie der Inhalt aufgebaut werden sein muss. Man könnte beispielsweise beim Element Alter sagen, dass nur ein Alter zwischen 0 und 130 Jahren erlaubt sein sollen (der älteste Mensch ist aktuell laut Wikipedia die Französin Jeanne Calment, die im Alter von 122 Jahren und 164 Tagen starb). Diese Einschränkungen werden auch Facets (Facetten) genannt und kommen bei der Erzeugung von neuen Datentypen zum Einsatz. Unter anderem kann man auf folgende Facets zurückgreifen:
- enumeration: Auflistung von Werten, die angenommen werden können
- fractionDigits: Anzahl an Nachkommastellen
- length: Anzahl der exakten Länge des Inhalts (Zeichen oder Listeneinträge)
- maxLength: Maximale Länge des Inhalts (Zeichen oder Listeneinträge)
- minLength: Minimale Länge des Inhalts (Zeichen oder Listeneinträge)
- maxExclusive: Obergrenze des numerischen Wertebereichs (echt kleiner)
- maxInclusive: Obergrenze des numerischen Wertebereichs (kleiner gleich)
- minExclusive: Untergrenze des numerischen Wertebereichs (echt größer)
- minInclusive: Untergrenze des numerischen Wertebereichs (größer gleich)
- pattern: Regulärer Ausdruck der erfüllt werden muss
- totalDigits: Anzahl an Ziffern (Vor- und Nachkomm
- whiteSpace: Behandlung von Leerzeichen, Tabs, neue Zeilen, Umbruch
Der generelle Aufbau eines neuen Datentyps mit Facets sieht bei einem simpleType folgendermaßen aus:
<xs:simpleType name="name"> <xs:restriction base="xs:source"> <xs:facet value="value" /> <xs:facet value="value" /> ... </xs:restriction> </xs:simpleType>
Sources sind dabei: string, Boolean, number, float, dateTime, Time, double …
Facets sind dabei: length, pattern, enumeration, minInclusive, maxExclusive …
Komplexe Typen – xs:complexeType
Im Gegensatz zu einfachen Typen können komplexe Typen selbst auch Unterelemente und Attribute besitzen.
Kompositoren
Bei komplexen Typen kann man die Reihenfolge der Informationselemente beliebig gestalten. Dafür stehen drei Kompositoren bereit:
- xs: sequence: Reihenfolge ist genau festgelegt
- xs:choice: Lediglich eines der Elemente in der aufgeführten Liste ist erlaubt
- xs:all: Alle Teile können in beliebiger Reihenfolge vorkommen oder auch nicht, höchstens aber einmal
Beispiel xs:sequence
<xs:complexType name=”geburtsdatum”> <xs:sequence> <xs:element name=”tag” type=”integer” /> <xs:element name=”monat” type=”integer” /> <xs:element name=”jahr” type=”integer” /> </xs:sequence> </xs:complexType>
Beispiel xs:choice
<xs:complexType name=”WahlDesWeltfussballers”> <xs:choice> <xs:element name=”Messi” /> <xs:element name=”Ronaldo” /> </xs:choice> </xs:complexType>
Beispiel xs:all
<xs:complexType name=”PartyTeilnehmer”> <xs:all> <xs:element name=”Anne” /> <xs:element name=”Max” /> <xs:element name=”Pia” /> <xs:element name=”Ronaldo” /> </xs:all> </xs:complexType>
Man kann die einzelnen Kompositoren auch verschachteln.
Erweiterungen
Nehmen wir einmal an, wir haben folgenden Datentyp:
<xs:complexType name=”Mitarbeiter”> <xs:sequence> <xs:element name=“vorname” type=”xs:string” /> <xs:element name=”nachname” type=”xs:string” /> <xs:element name=”geburtstag” type=”xs:date” /> </xs:sequence> </xs:complexType>
Nun haben wir neben normalen Mitarbeitern in einer Firma auch spezielle Mitarbeiter, wie beispielsweise Abteilungsleiter. Diese besitzen natürlich auch einen Namen und Geburtstag. Möchte man nun einen neuen Abteilungsleiter-Typ müsste man also diese Elemente ebenfalls aufnehmen, was zu einer gewissen Redundanz führen würde. Deshalb kann man den schon vorhandenen Mitarbeiter-Typ folgendermaßen erweitern:
<xs:complexType name=“Abteilungsleiter“> <xs:complexContent> <xs:extension base=“Mitarbeiter“> <xs:sequence> <xs:element name=“budget“ type=“xs:integer“ /> <xs:element name=“AnzahlMitarbeiter“ type=“xs:integer“ /> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType>
Der Typ Abteilungsleiter besitzt damit alle Elemente und Attribute von Mitarbeiter und hat darüber hinaus auch noch ein Element Budget und ein Element für die Anzahl der Mitarbeiter die in seiner Abteilung sind.
Schlüsselelemente
Den Schlüsselbegriff, den einige vielleicht aus der Welt der relationalen Datenbanken kennen könnten, wird durch XML Schema erstmals in die XML-Welt eingeführt. Ein Schlüssel zeichnet sich aus, dass er eindeutig und damit identifizierend ist. Dafür stehen in XML Schema die drei Elemente <xs:key>, <xs:unique> und <xs:keyref> bereit.
Generell besteht die Schlüsseldefinition in XML Schema immer aus drei Teilen:
-
- einem Kontext: innerhalb diesen gilt die Schlüsseleigenschaft
- einem Selektor: definiert relativ zum Kontext eine Menge von Knoten (Zielmenge)
- eine Menge von Feldern (Elementinhalte und Attribute), bestimmen den eindeutigen Indentifikator für jedes Element der Zielmenge
Konkret an dem Mitarbeiterbeispiel von weiter oben, wobei dieses um ein Element MitarbeiterNr. Ergänzt wurde, dass gleichzeitig auch der Schlüssel sein soll:
<?xml version=“1.0“ encoding=“UTF-8“ ?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xs:element name=“Mitarbeiter“> <xs:complexType> <xs:sequence> <xs:element name=“mitarbeiternr” type=”xs:integer” /> <xs:element name=“vorname” type=”xs:string” /> <xs:element name=”nachname” type=”xs:string” /> <xs:element name=”geburtstag” type=”xs:date” /> </xs:sequence> </xs:complexType> <xs:key name=“mitarbeiternr“> <xs:selector xpath=“mitarbeiter“ /> <xs:field xpath=“mitarbeiternr“ /> </xs:key> </xs:element> </xs:schema>
Der Schlüssel ist in diesem Fall die mitarbeiternr, was im <xs:key>-Element festgelegt wird. Dafür wird im Element <xs:selector> angegeben, für welches Element diese Einschränkung gilt und das ist in diesem Fall für alle Mitarbeiter. Durch das Element <xs:field> wird nun im Element Mitarbeiter das Element bestimmt, das eindeutig sein muss. Das ansprechen der jeweiligen Mitarbeiter wird über einen XPATH-Ausdruck geregelt.
Nutzt man das <xs:key>-Element für die Schlüsseldefinition, dann wird als Eigenschaft gefordert, dass alle Schlüsselbestandteile vorhanden und ungleich „NULL“ sind. Das Element <xs:unique> hingegen funktioniert ebenfalls wie das Element <xs:key> stellt aber abgeschwächte Bedingungen an die Schlüsseleigenschaften. So sind bei <xs:unique> auch Nullwerte zugelassen.
Mit dem Element <xs:keyref> lässt sich nun noch eine Fremdschlüsselbeziehung konstruieren, in dem auf den jeweiligen Schlüssel referenziert wird. Beispiel mit den Mitarbeitern, wobei nun angenommen wird, es existiert auch noch ein komplexer Typ Lohnauszug innerhalb des Schemas, in dem die Lohnhöhe mit der Mitarbeiternummer abgespeichert ist:
<xs:keyref name=“mitarbeiterfremdschlüssel“ refer=“mitarbeiternr“> <xs:selector xpath=“mitarbeiter/lohnauszug“ /> <xs:field xpath=“mitabnr“ /> </xs:keyref>
Das Beispiel zu Beispiel xs:all ist falsch. Sonst gut erlärt.
Hey Falko,
danke für den Hinweis, da ist wohl beim Kopieren etwas schiefgegangen 🙂 Ich habe das Beispiel nun ersetzt, jetzt sollte es passen!
Beste Grüße
Christian
Super erklärt! Wann wird das Tutorial eigentlich fortgesetzt? 🙂
LG
Hi Miya,
momentan ist leider Pause. Ich weiß leider noch nicht, wann es weiter geht.
Beste Grüße
Christian
Das Beispiel:all ist immer noch flasch! etwas irritierent .
Gute Erklärung.
Gruß
chris
Hey Chris,
danke für den Hinweis, nun sollte aber hoffentlich alles stimmen 😀
Beste Grüße
Christian
Ich glaube nur der beschreibende Satz ist irreführend. „Beliebige Reihenfolge“ passt nicht zu maximal ein Teil:
3. xs:all: Alle Teile können in beliebiger Reihenfolge vorkommen oder auch nicht, höchstens aber einmal
Ist es so gemeint?
3.xs:all: Alle Teile können in beliebiger Reihenfolge vorkommen oder auch nicht, aber mindestens ein Teil muss vorhanden sein.
Gruss,
Anke
In Zeile 3 des ersten Beispiels für Erweiterungen ist ein Tippfehler drin 😀
sonst aber, soweit ich das beurteilen kann, eine super Erklärung.
Hey Tobias,
ist ausgebessert, vielen Dank für deinen Hinweis.
Beste Grüße
Christian