Eine XML-Datei kann wohlgeformt sein und trotzdem Unsinn enthalten: eine Menge von null, einen Preis mit drei Nachkommastellen, ein Datum im falschen Format. XML Schema, kurz XSD, beschreibt verbindlich, welche Elemente in welcher Reihenfolge vorkommen dürfen und welche Werte darin erlaubt sind. Ein Parser prüft das Dokument dann automatisch, bevor ein Programm die Daten weiterverarbeitet. Wer heute mit E-Rechnungen, Behördenschnittstellen oder Bankformaten arbeitet, stößt fast zwangsläufig auf XSD-Dateien.
Vom Primer 2001 zu XSD 1.1
Das W3C hat XML Schema am 2. Mai 2001 als Empfehlung (Recommendation) veröffentlicht, aufgeteilt in drei Dokumente: Teil 0 ist ein nicht normativer Einführungstext (Primer), Teil 1 regelt die Strukturen, Teil 2 die Datentypen. Am 28. Oktober 2004 folgte eine Second Edition, die Fehler korrigierte, aber keine neue Sprachversion war. Der Primer ist bis heute der beste Einstieg; sein Bestellbeispiel mit der Datei po.xsd zeigt fast alles, was man im Alltag braucht.
Am 5. April 2012 erklärte das W3C XSD 1.1 (Teil 1 Structures, Teil 2 Datatypes) zur Empfehlung. Die wichtigsten Neuerungen sind Assertions, also Bedingungen in XPath 2.0, bedingte Typzuweisung abhängig etwa von einem Attributwert, offene Inhaltsmodelle, das Element xs:override sowie neue Datentypen wie dateTimeStamp, dayTimeDuration und yearMonthDuration. In der Praxis ist XSD 1.0 dennoch der verbreitete Standard, weil viele Werkzeuge 1.1 nicht beherrschen (dazu unten mehr).
Einfache und komplexe Typen am Beispiel einer Bestellung
XSD unterscheidet zwei Arten von Typen. Ein einfacher Typ (simpleType) beschreibt nur Text ohne Kindelemente und Attribute, etwa eine Artikelnummer oder einen Betrag. Ein komplexer Typ (complexType) darf Kindelemente und Attribute haben. Das folgende Schema beschreibt eine kleine Bestellung mit beliebig vielen Positionen:
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:b='https://example.org/bestellung'
targetNamespace='https://example.org/bestellung'
elementFormDefault='qualified'>
<xs:element name='bestellung' type='b:BestellungTyp'/>
<xs:complexType name='BestellungTyp'>
<xs:sequence>
<xs:element name='kunde' type='xs:string'/>
<xs:element name='position' type='b:PositionTyp' maxOccurs='unbounded'/>
</xs:sequence>
<xs:attribute name='datum' type='xs:date' use='required'/>
</xs:complexType>
<xs:complexType name='PositionTyp'>
<xs:sequence>
<xs:element name='artikel' type='b:ArtikelNr'/>
<xs:element name='menge'>
<xs:simpleType>
<xs:restriction base='xs:positiveInteger'>
<xs:maxInclusive value='999'/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name='preis' type='b:Betrag'/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name='ArtikelNr'>
<xs:restriction base='xs:string'>
<xs:pattern value='[A-Z]{2}-\d{4}'/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='Betrag'>
<xs:restriction base='xs:decimal'>
<xs:minInclusive value='0'/>
<xs:fractionDigits value='2'/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Ein passendes Instanzdokument sieht so aus:
<?xml version='1.0' encoding='UTF-8'?>
<bestellung xmlns='https://example.org/bestellung'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='https://example.org/bestellung bestellung.xsd'
datum='2026-10-14'>
<kunde>Muster GmbH</kunde>
<position>
<artikel>KB-1042</artikel>
<menge>3</menge>
<preis>19.90</preis>
</position>
</bestellung>
Zwei Stilfragen lohnen einen Gedanken. Benannte Typen wie PositionTyp lassen sich wiederverwenden und erweitern; anonyme Typen direkt im Element (hier bei menge) halten kleine Einschränkungen dort, wo sie gebraucht werden. Für alles, was an mehr als einer Stelle vorkommt, sind benannte Typen die bessere Wahl. Und sequence erzwingt die Reihenfolge der Kindelemente, choice erlaubt genau eine von mehreren Alternativen, all jede Reihenfolge.
Datentypen und Facetten
Teil 2 definiert 19 primitive Datentypen, darunter string, boolean, decimal, double, dateTime, date, anyURI und base64Binary. Davon abgeleitet sind eingebaute Typen wie integer, positiveInteger oder token. Eigene Typen entstehen durch Einschränkung (restriction) mit sogenannten Facetten:
| Facette | Wirkung | Beispiel |
|---|---|---|
| length, minLength, maxLength | Länge in Zeichen (bei Listen: Anzahl Einträge) | Postleitzahl genau 5 Zeichen |
| pattern | regulärer Ausdruck, muss den ganzen Wert treffen | [A-Z]{2}-\d{4} |
| enumeration | feste Werteliste | Währung EUR, CHF, USD |
| minInclusive, maxInclusive, minExclusive, maxExclusive | Wertebereich | Menge 1 bis 999 |
| totalDigits, fractionDigits | Stellen bei Dezimalzahlen | Betrag mit max. 2 Nachkommastellen |
| whiteSpace | Umgang mit Leerraum (preserve, replace, collapse) | Leerzeichen am Rand ignorieren |
| assertion, explicitTimezone | nur XSD 1.1: XPath-Bedingung bzw. Zeitzonenpflicht | Datum muss Zeitzone haben |
Ein häufiger Irrtum betrifft pattern: Der Ausdruck ist implizit verankert, ^ und $ sind nicht nötig. Und xs:date erwartet immer das ISO-Format 2026-10-14, nie 14.10.2026.
Namespaces: targetNamespace und elementFormDefault
Das Attribut targetNamespace legt fest, zu welchem Namensraum die im Schema definierten Elemente gehören. Im Instanzdokument muss derselbe URI auftauchen, im Beispiel als Standardnamensraum. elementFormDefault='qualified' sorgt dafür, dass auch die lokalen Kindelemente wie kunde im Namensraum liegen; ohne diese Angabe stünden sie im leeren Namensraum, was in der Praxis fast nie gewollt ist und zu rätselhaften Fehlermeldungen führt. Das Attribut xsi:schemaLocation enthält Wertepaare aus Namensraum-URI und Dateipfad; für Dokumente ohne Namensraum gibt es xsi:noNamespaceSchemaLocation. Beides ist nur ein Hinweis, kein Zwang: Ein Prüfprogramm darf das Schema auch anders beziehen. Grundlagen zu Elementen, Attributen und Namensräumen erklärt der Beitrag XML-Grundlagen.
Validieren mit xmllint und im Editor
Unter Linux und macOS ist xmllint aus libxml2 meist schon installiert. Die Prüfung gegen das Schema oben:
xmllint --noout --schema bestellung.xsd bestellung.xml
bestellung.xml validates
Ändert man die Artikelnummer in kb-1042, die Menge in 0 und den Preis in 19.999, meldet xmllint drei Fehler mit Zeilennummer: die verletzte pattern-Facette, den ungültigen Wert für den lokalen Typ und zu viele Nachkommastellen. Genau dafür lohnt sich ein Schema: Fehler fallen beim Import auf und nicht erst in der Buchhaltung.
Wichtig: libxml2 unterstützt nur XSD 1.0. Ein Schema mit xs:assert scheitert dort, und dasselbe gilt für PHP-Funktionen wie DOMDocument::schemaValidate, die auf libxml2 aufbauen. Wer XSD 1.1 braucht, greift etwa zum kommerziellen Saxon-EE: vollständige Unterstützung seit Version 9.5, seit 9.8 ist 1.1 dort sogar der Standard. XML-Editoren wie Oxygen oder die XML-Erweiterungen für VS Code und IntelliJ prüfen schon beim Tippen und bieten Autovervollständigung aus dem Schema, was bei großen Schemas mehr bringt als jede Dokumentation.
Eine 1.1-Assertion, die sich in den Typ BestellungTyp nach dem Attribut einfügen lässt, begrenzt etwa die Zahl der Positionen:
<xs:assert test='count(b:position) le 50'/>
XRechnung: XSD plus Schematron im Einsatz
Das bekannteste deutsche Beispiel ist die E-Rechnung. Eine XRechnung ist ein XML-Dokument in einer von zwei Syntaxen: OASIS UBL 2.1 (Invoice oder CreditNote) oder UN/CEFACT Cross Industry Invoice. Die Koordinierungsstelle für IT-Standards (KoSIT) veröffentlicht dafür eine Prüfkonfiguration unter Apache-2.0-Lizenz, die die XML-Schemas der beiden Syntaxen mit Schematron-Regeln für die Norm EN 16931 und die deutschen XRechnung-Vorgaben kombiniert. Das zeigt die Arbeitsteilung gut: XSD prüft Struktur und Datentypen, Schematron prüft Geschäftsregeln wie „Summe der Positionen gleich Rechnungsbetrag“. Wer eigene Rechnungen erzeugt, sollte beide Stufen mit dem KoSIT-Validator laufen lassen, nicht nur xmllint gegen das UBL-Schema.
XSD, DTD, RELAX NG oder JSON Schema?
| Sprache | Syntax | Datentypen | Namespaces | Typischer Einsatz |
|---|---|---|---|---|
| DTD | eigene, nicht XML | praktisch keine (nur Text, IDs, Aufzählungen bei Attributen) | nicht unterstützt | Altbestände, Entity-Definitionen |
| XSD 1.0/1.1 | XML | umfangreich, mit Facetten | voll | Behörden, Finanzwesen, E-Rechnung, SOAP, Datenbindung in Java/.NET |
| RELAX NG | XML oder kompakte Syntax | über XSD-Datentypen einbindbar | voll | Dokumentformate, etwa DocBook 5 (normatives Schema) |
| JSON Schema | JSON | JSON-Typen plus Formate | entfällt | REST-APIs, Konfigurationsdateien |
RELAX NG entstand 2001 bei OASIS und ist als ISO/IEC 19757-2 genormt. Es ist deutlich leichter zu lesen und zu schreiben als XSD, vor allem in der kompakten Syntax. JSON Schema liegt aktuell in der Version 2020-12 vor und ist das Mittel der Wahl, sobald die Daten ohnehin JSON sind.
Die Empfehlung ist klar: Schreibt eine Schnittstelle, Norm oder Behörde XSD vor, gibt es keine Wahl. Für ein eigenes, neues XML-Format ohne solche Vorgaben ist RELAX NG angenehmer und weniger fehleranfällig; XSD lohnt sich dann, wenn Werkzeuge daraus Java- oder C#-Klassen generieren sollen. DTDs braucht man nur noch für Altbestände. Und wer gerade ein neues Datenformat für eine Web-API entwirft, sollte prüfen, ob es überhaupt XML sein muss. Damit das Schema mit Umlauten und Sonderzeichen korrekt arbeitet, gehört in jedes Dokument eine saubere Kodierung; Hintergründe dazu stehen im Artikel über Zeichenkodierung und UTF-8.
Quellen
- W3C: XML Schema Part 0: Primer Second Edition (Recommendation, 28. Oktober 2004)
- W3C: Publikationsgeschichte XML Schema Part 0
- W3C News: Two XML Schema Specifications are Recommendations (5. April 2012)
- W3C: XSD 1.1 Part 2: Datatypes
- xmllint(1) Manpage
- Saxonica: XML Schema 1.1 in Saxon
- KoSIT: Validator-Konfiguration XRechnung (GitHub)
- RELAX NG Home Page







