Als je ooit een enterprise XML API hebt geïntegreerd en een cryptische afwijzing ontving met "element 'Quantity' is not valid in this context", heb je al kennisgemaakt met XSD — ook al wist je de naam niet. XML Schema Definition (XSD) is de taal die exact beschrijft hoe geldige XML eruit ziet: welke elementen verplicht zijn, welke datatypes ze bevatten, hoe vaak ze mogen voorkomen en welke waarden zijn toegestaan. Deze gids behandelt hoe je schemas schrijft, er tegenaan valideert en de fouten afhandelt die je onvermijdelijk zult tegenkomen.

XSD is gedefinieerd door de W3C XML Schema-specificatie, voor het eerst gepubliceerd in 2001. De XML Schema Part 2: Datatypes aanbeveling definieert het ingebouwde typesysteem. Het is uitgebreid en enigszins cryptisch om met de hand te schrijven, maar extreem precies — precies wat je nodig hebt bij het uitwisselen van gegevens tussen systemen die geen ambiguïteit tolereren.

Waarom XML überhaupt valideren?

  • Vang datafouten vroeg op. Validatie aan de API-grens betekent dat een ontbrekend verplicht veld onmiddellijk mislukt met een duidelijke foutmelding — niet 3 stappen later wanneer je database-insert een constraint-schending gooit.
  • Documenteer het contract. Een XSD-schema is uitvoerbare documentatie. In tegenstelling tot een Word-document dat verouderd raakt, is een schema altijd actueel omdat het systeem het afdwingt.
  • Interoperabiliteit. In B2B-integraties — facturering, orderbeheer, gezondheidszorg — valideren beide partijen tegen een gedeeld gepubliceerd schema. Als het valideert, kunnen beide kanten het verwerken.
  • Beveiliging. Validatie wijst verkeerd gevormde invoer af voordat die de bedrijfslogica bereikt, waardoor het aanvalsoppervlak voor XML-injectieaanvallen wordt verkleind.

Een compleet XSD-schemavoorbeeld

Laten we een schema bouwen voor een productcatalogus. Dit behandelt de meestgebruikte XSD-functies — eenvoudige typen, complexe typen, sequenties, attributen, kardinaliteit en datatype-beperkingen:

xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <!-- Root element -->
  <xs:element name="catalog">
    <xs:complexType>
      <xs:sequence>
        <!-- One or more product elements -->
        <xs:element name="product" type="ProductType" minOccurs="1" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="version" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>

  <!-- Product complex type -->
  <xs:complexType name="ProductType">
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="description" type="xs:string" minOccurs="0"/>
      <xs:element name="price" type="PriceType"/>
      <xs:element name="stock" type="xs:nonNegativeInteger"/>
      <xs:element name="categories" type="CategoriesType"/>
    </xs:sequence>
    <xs:attribute name="id" type="ProductIdType" use="required"/>
    <xs:attribute name="status" type="ProductStatusType" use="optional" default="active"/>
  </xs:complexType>

  <!-- Price with currency attribute -->
  <xs:complexType name="PriceType">
    <xs:simpleContent>
      <xs:extension base="xs:decimal">
        <xs:attribute name="currency" type="CurrencyCodeType" use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <!-- Categories — zero or more category strings -->
  <xs:complexType name="CategoriesType">
    <xs:sequence>
      <xs:element name="category" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <!-- Product ID: alphanumeric, starts with P, 4-10 chars -->
  <xs:simpleType name="ProductIdType">
    <xs:restriction base="xs:string">
      <xs:pattern value="P[A-Z0-9]{3,9}"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- Allowed product statuses -->
  <xs:simpleType name="ProductStatusType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="active"/>
      <xs:enumeration value="discontinued"/>
      <xs:enumeration value="out_of_stock"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- ISO 4217 currency codes — a subset -->
  <xs:simpleType name="CurrencyCodeType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="USD"/>
      <xs:enumeration value="EUR"/>
      <xs:enumeration value="GBP"/>
      <xs:enumeration value="JPY"/>
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

Veel om te verwerken. Laten we de sleutelconcepten doornemen voordat we validatie in actie laten zien.

Sleutel-XSD-concepten uitgelegd

  • xs:element. Declareert een element. minOccurs en maxOccurs regelen de kardinaliteit. Standaard voor beide is 1. Gebruik maxOccurs="unbounded" voor onbeperkte herhaling.
  • xs:complexType. Een element dat child-elementen bevat of attributen heeft. Eenvoudige elementen (alleen tekst) gebruiken xs:simpleType of direct een ingebouwd XSD-type.
  • xs:sequence. Child-elementen moeten in de gedefinieerde volgorde verschijnen. Het alternatief is xs:all (willekeurige volgorde, elk maximaal één keer) of xs:choice (precies één van de vermelde elementen).
  • xs:attribute use="required". Maakt het attribuut verplicht. use="optional" (standaard) staat afwezigheid toe. Voeg default="value" toe voor een standaardwaarde bij afwezigheid.
  • xs:restriction. Beperkt een basistype. Veelgebruikte facetten: xs:pattern (regex), xs:enumeration (toegestane waarden), xs:minInclusive/xs:maxInclusive (numerieke grenzen), xs:minLength/xs:maxLength (stringlengte).
  • xs:simpleContent + xs:extension. De manier om attributen toe te voegen aan een element dat ook tekstinhoud heeft — zoals gebruikt in het bovenstaande PriceType waar <price currency="USD">149.99</price> zowel tekst als een attribuut heeft.

Ingebouwde XSD-datatypes die je echt zult gebruiken

  • xs:string — willekeurige tekstinhoud
  • xs:integer — gehele getallen (positief, negatief of nul)
  • xs:nonNegativeInteger — gehele getallen ≥ 0 (goed voor hoeveelheden, tellingen)
  • xs:decimal — decimaal met willekeurige precisie (goed voor prijzen)
  • xs:booleantrue of false (accepteert ook 1 en 0)
  • xs:dateISO 8601-datum: 2024-01-15
  • xs:dateTime — ISO 8601 datetime: 2024-01-15T09:30:00Z
  • xs:anyURI — een URI/URL
  • xs:base64Binary — Base64-gecodeerde binaire gegevens

XML valideren tegen een XSD in Python (lxml)

Python's standaard xml.etree.ElementTree ondersteunt geen XSD-validatie. Daarvoor heb je lxml nodig. De afhankelijkheid is de moeite waard — lxml's validatiemeldingen zijn gedetailleerd en wijzen je naar de exacte regel die het probleem veroorzaakt:

bash
pip install lxml
python
from lxml import etree

# Load the schema
with open('catalog.xsd', 'rb') as f:
    schema_doc = etree.parse(f)
schema = etree.XMLSchema(schema_doc)

# Valid XML
valid_xml = """<?xml version="1.0"?>
<catalog version="1.0">
  <product id="P0012" status="active">
    <name>Mechanical Keyboard</name>
    <price currency="USD">189.00</price>
    <stock>42</stock>
    <categories>
      <category>Electronics</category>
      <category>Peripherals</category>
    </categories>
  </product>
</catalog>"""

xml_doc = etree.fromstring(valid_xml.encode())
if schema.validate(xml_doc):
    print("Valid!")
else:
    for error in schema.error_log:
        print(f"Error at line {error.line}: {error.message}")

# Invalid XML — missing required 'stock', bad product ID format
invalid_xml = """<?xml version="1.0"?>
<catalog version="1.0">
  <product id="BADID">
    <name>Test Product</name>
    <price currency="USD">10.00</price>
    <categories/>
  </product>
</catalog>"""

invalid_doc = etree.fromstring(invalid_xml.encode())
schema.validate(invalid_doc)
for error in schema.error_log:
    print(f"Line {error.line}: {error.message}")
# Line 3: Element 'product', attribute 'id': 'BADID' is not a valid value of the atomic type 'ProductIdType'.
# Line 7: Element 'categories': Missing child element(s). Expected is ( category ).  ← if minOccurs > 0

Valideren in Java — JAXP

Java heeft ingebouwde XSD-validatie via de JAXP-validatie-API (geen externe bibliotheek nodig). Dit is het patroon dat wordt gebruikt in enterprise Java-applicaties en Spring Boot XML-verwerking:

java
import javax.xml.XMLConstants;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import org.xml.sax.SAXException;
import java.io.IOException;

SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File("catalog.xsd"));
Validator validator = schema.newValidator();

try {
    validator.validate(new StreamSource(new File("catalog.xml")));
    System.out.println("Valid!");
} catch (SAXException e) {
    System.out.println("Validation error: " + e.getMessage());
} catch (IOException e) {
    System.out.println("IO error: " + e.getMessage());
}

XML Schema vs JSON Schema

Als je JSON Schema hebt gebruikt, zal XSD in doel vertrouwd aanvoelen maar heel anders in syntaxis. Een snelle vergelijking:

  • Syntaxis. XSD is XML; JSON Schema is JSON. XSD is uitgebreider maar integreert natuurlijk in XML-toolchains.
  • Volwassenheid. XSD 1.0 bestaat al sinds 2001 — tientallen jaren aan tooling, validators en bibliotheekondersteuning. JSON Schema evolueert al sinds 2009 en bereikte pas de afgelopen jaren een stabiel concept.
  • Typesysteem. XSD heeft een rijker ingebouwd typesysteem: xs:date, xs:decimal, xs:anyURI zijn ingebouwd. JSON Schema vertrouwt op formaatannotaties voor datums en URI's, die validators al dan niet kunnen afdwingen.
  • Namespace-ondersteuning. XSD heeft native ondersteuning voor XML-namespaces. JSON Schema heeft geen equivalent concept.
  • Wanneer wat te gebruiken. Als je in een XML-ecosysteem werkt, gebruik XSD. Als je in een JSON-ecosysteem werkt, gebruik JSON Schema. Meng ze niet.

Veelvoorkomende validatiefouten en hoe je ze oplost

  • "Element X is not expected." Het element verschijnt buiten volgorde in een xs:sequence, of is helemaal niet gedefinieerd in het schema. Controleer de elementnaam en de positie ten opzichte van de siblingelementen.
  • "The value Y of attribute Z is not valid." De attribuutwaarde komt niet overeen met het type of de enumeratie. Komt vaak voor bij statusvelden en valutacodes met typfouten.
  • "Missing child element(s)." Een verplicht child-element (minOccurs > 0) ontbreekt. Controleer het schema voor welke elementen verplicht zijn onder het bovenliggende element.
  • "Not a valid value of the atomic type." Een eenvoudige type-beperking is mislukt — patroon, bereik of enumeratie. Controleer het schema voor de xs:restriction op dat type.
  • "Content model is not determinist." Ambigu schema — de validator kan niet bepalen welke tak van toepassing is. Meestal veroorzaakt door twee opties met dezelfde tagnaam in een xs:choice.

Gerelateerde tools

Werkt u met XML-schemas? Deze tools kunnen helpen: XML Validator voor snelle welgevormdheidschecks, XML Formatter om dichte XML leesbaar te maken vóór het debuggen, XML Schema Generator om automatisch een XSD te genereren vanuit een voorbeeld-XML-document, en XML to JSON wanneer je liever met JSON Schema werkt.

Samenvatting

XSD is uitgebreid, maar het is het juiste gereedschap voor het definiëren van strikte XML-contracten in enterprise- en B2B-contexten. De sleutelpatronen om te onthouden: gebruik xs:sequence met minOccurs/maxOccurs om de structuur te controleren, gebruik xs:restriction met xs:enumeration en xs:pattern voor waardenbeperking, en gebruik lxml in Python voor validatie met duidelijke foutmeldingen. Zodra je een werkend schema hebt, wordt het de enige bron van waarheid waarnaar beide kanten van een integratie kunnen verwijzen — wat veel heen-en-weer debuggen bespaart.