5.3 XML-Schema’s (XSD)

Kan XML-Schema’s (XSD’s) lezen, begrijpen en opstellen.


Het probleem: XML is te vrij

In sectie 5.1 heb je geleerd hoe je met XML gegevens structureert. Maar XML zelf legt alleen syntaxregels op — welgevormdheid. Het zegt niets over welke elementen en attributen zijn toegestaan, in welke volgorde ze mogen voorkomen of welke waarden geldig zijn.

Stel: twee gemeentelijke systemen wisselen persoonsgegevens uit via XML. Systeem A stuurt:

<persoon>
  <voornaam>Jan</voornaam>
  <achternaam>de Vries</achternaam>
  <geboortedatum>1985-03-15</geboortedatum>
</persoon>

Maar systeem B verwacht:

<persoon>
  <naam>
    <voornaam>Jan</voornaam>
    <achternaam>de Vries</achternaam>
  </naam>
  <geboortedatum>15-03-1985</geboortedatum>
</persoon>

Beide documenten zijn welgevormd XML. Maar ze zijn niet compatibel: de structuur verschilt (wel of geen <naam>-omhulsel) en het datumformaat is anders. Hoe spreek je af welke structuur en welke waarden toegestaan zijn? Hoe controleer je dat automatisch?

De oplossing: een XML-Schema

Een XML-Schema is een formele beschrijving van de toegestane structuur en inhoud van een XML-document. Het is als een blauwdruk of contract: het legt vast welke elementen er mogen zijn, in welke volgorde, hoe vaak, en welk type waarde ze mogen bevatten.

Met een XML-Schema kun je automatisch controleren of een XML-document geldig is — dit heet validatie. Als een document niet voldoet aan het schema, wordt het afgekeurd met een foutmelding die precies aangeeft wat er mis is.

Wat is XSD precies?

XSD staat voor XML-Schema Definition. Als we het dus over een XSD-bestand of XSD-Schema hebben dan bedoelen we dus gewoon ook een XML-Schema.

XML-Schema is een taal om schema’s mee te schrijven — en het bijzondere is: een XSD-bestand is zelf ook XML. Je gebruikt dus XML om te beschrijven hoe andere XML-documenten eruit moeten zien. Het is zelfs zo dat een XML-Schema ook weer aan een XML-Schema moet voldoen.

EigenschapUitleg
VoluitXML-Schema Definition
Bestandsextensie.xsd
FormaatXML (een XSD is zelf een geldig XML-document)
DoelFormeel beschrijven welke structuur en waarden een XML-document mag hebben
ToepassingValidatie: automatisch controleren of een XML-document aan de regels voldoet
BeheerderW3C (World Wide Web Consortium), dezelfde organisatie achter XML zelf

Een eerste voorbeeld

Stel, je wilt vastleggen dat een <persoon> precies een <voornaam>, een <achternaam> en een <geboortedatum> moet bevatten. Dan zou je dat als volgt kunnen doen:

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

  <xs:element name="persoon">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="voornaam" type="xs:string"/>
        <xs:element name="achternaam" type="xs:string"/>
        <xs:element name="geboortedatum" type="xs:date"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>
RegelBetekenis
<xs:schema xmlns:xs="...">Het root-element van elk XSD-bestand; de prefix xs verwijst naar de XML-Schema-namespace
<xs:element name="persoon">Op root niveau moet een element <persoon> bestaan
<xs:complexType>Het element <persoon> heeft een complexe opbouw (het bevat kind-elementen en/of attributen)
<xs:sequence>De kind-elementen moeten in de opgegeven volgorde voorkomen
<xs:element name="voornaam" type="xs:string"/>Er moet een element <voornaam> zijn, met een tekstwaarde
<xs:element name="geboortedatum" type="xs:date"/>Er moet een element <geboortedatum> zijn, met een datumwaarde (formaat JJJJ-MM-DD)

Een volgens dit XML-Schema correct XML-document kan er als volgt uitzien:

<persoon>
  <voornaam>Jan</voornaam>
  <achternaam>de Vries</achternaam>
  <geboortedatum>1985-03-15</geboortedatum>
</persoon>

Hieronder ook een voorbeeld van een XML-document waarin, volgens het XML-Schema, twee fouten staan:

<persoon>
  <achternaam>de Vries</achternaam>
  <voornaam>Jan</voornaam>
  <geboortedatum>15 maart 1985</geboortedatum>
</persoon>

Een XML-Parser kan op basis van voorgaand XML-fragment bijv. de volgende foutmelding genereren:

Validate

Dit betekent zoveel als

  1. het element <achternaam> komt vóór het element <voornaam>. Het schema eist echter een volgorde waarbij <achternaam> na het element <voornaam> maar ook vóór het element <geboortedatum> staat.
  2. Het element <geboortedatum> heeft de waarde "15 maart 1985". Het schema eist echter een waarde die voldoet aan het datatypexs:date wat betekent dat een formaat als JJJJ-MM-DD moet worden gebruikt.

Let op! We gaven eerder al aan dat XML case-sensitive is. Definieer je in je XML-Schema dus als volgt een element <xs:element name="voornaam" type="xs:string"/> dan kan je in je XML-bestand niet het element <Voornaam> gebruiken. Tenminste niet als je wil dat het aan een XML-Schema voldoet. Dit betekent echter ook dat je in je XML-Schema naast het element <voornaam> ook een element <Voornaam> kan definiëren. Doe dat echter met uiterste terughoudendheid. Voor een computer is het verschil heel duidelijk maar voor een mens die het XML-Bestand moet interpreteren is dat heel wat minder het geval.

Welgevormd vs. valide

In het onderdeel ‘5.1 XML syntax en structuur’ hebben we de welgevormdheid van een XML-bestand behandelt. Daarbij gaven we aan dat een XML-bestand daarnaast ook nog valide kan zijn. Dat is een cruciaal onderscheid:

BegripBetekenisGecontroleerd door
Welgevormd (well-formed)Het document voldoet aan de XML-syntaxregels (zie 5.1)Elke XML-Parser
ValideHet document voldoet aan de XML-syntaxregels en aan een specifiek schema (XSD)Een XML-Parser met het bijbehorende XML-Schema

Een document moet eerst welgevormd zijn voordat het gevalideerd kan worden. De volgende beslisboom is dus van toepassing:

Stap 1: Is het welgevormd?  → Nee  → Afgekeurd (syntaxfout)
                            → Ja   → Stap 2: Is het geldig volgens het schema?
                                              → Nee  → Afgekeurd (validatiefout)
                                              → Ja   → Geaccepteerd ✓

Waarom is XSD belangrijk voor StUF?

StUF-berichten worden gedefinieerd door een set XSD-schema’s:

  1. Contract tussen systemen — Het schema legt exact vast hoe een bericht eruit moet zien. Als een leverancier een systeem bouwt dat StUF-berichten verstuurt, moet het elk bericht valideren tegen het relevante StUF-schema.
  2. Automatische validatie — Vóórdat een bericht verwerkt wordt, kan het ontvangend systeem het automatisch valideren. Bij een succesvolle validatie weet dat systeem dat het dat bericht zonder problemen kan verwerken.
  3. Documentatie — De XSD-schema’s zijn tegelijk de technische documentatie van de standaard.
  4. Gelaagde opbouw — StUF-schema’s zijn modulair opgebouwd in lagen (basisschema → sectormodel → koppelvlak), mogelijk gemaakt door XSD-mechanismes als import, include en type-overerving.

De structuur van een XSD-bestand

Elk XSD-bestand is zelf een XML-document en kan zelf, zoals al eerder opgemerkt, ook weer tegen een XML-Schema (ook wel een meta-schema genaamd) gevalideerd worden. Het root-element is altijd <xs:schema>:

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

  <!-- Hier komen je definities -->

</xs:schema>
OnderdeelBetekenis
<xs:schema>Het root-element van elk XSD-bestand
xmlns:xs="http://www.w3.org/2001/XMLSchema"De namespace-declaratie die de prefix xs koppelt aan de XML-Schema-namespace

De prefix xs is conventie — je kunt ook xsd kiezen. In de praktijk zie je zowel xs: als xsd:. Soms zelfs beide in een set van XML-Schema’s.

Elementen definiëren met xs:element

De meest fundamentele bouwsteen van XSD is het element om een element mee te definiëren:

<xs:element name="voornaam" type="xs:string"/>

Dit zegt: “Er moet een element <voornaam> bestaan, en de inhoud moet tekst zijn.”

AttribuutBetekenisVoorbeeld
nameDe naam van het element in het met het XML-Schema gekoppelde XML-bestandname="voornaam"
typeHet datatype van de inhoudtype="xs:string"

Een <xs:element ...> element direct onder het <xs:schema> element definieert een root element, een element waarmee een XML-bestand mag beginnen. Dat mogen er meer dan één zijn zodat je bij het aanmaken van een XML-Bestand op basis van dat schema kunt kiezen welke je root element is.

Ingebouwde datatypen

In het voorbeeld in de voorgaande paragraaf werd m.b.v. het type attribuut het datatype van het <voornaam> element gedefinieerd. In dit geval het xs:string datatype, tekst dus. Dit is niet het enige datatype dat je kunt definiëren. Het W3C heeft naast de XML-Schema standaard nl. een uitgebreide set van ingebouwde datatypen gedefinieerd. Een parser die XSD‑validatie ondersteunt, moet dus ook o.a. de volgende datatypes kennen:

Teksttypen:

TypeBeschrijvingVoorbeeldwaarde
xs:stringTekenreeksen (ondersteunt spaties, tabs, etc.)." Jan de Vries "
xs:normalizedStringTekenreeksen waarin regeleinden en tabs zijn vervangen door spaties." Jan de Vries "
xs:tokenTekenreeksen waarbij ook overtollige spaties aan het begin, einde en dubbele spaties worden verwijderd."Jan de Vries"

Numerieke typen:

TypeBeschrijvingVoorbeeldwaarde
xs:integerGehele getallen (positief, negatief of nul).42, -7
xs:positiveIntegerGehele getallen > 0.1, 100
xs:decimalGetallen met een vaste komma.3.14, -0.5

Datum- en tijdtypen:

TypeBeschrijvingVoorbeeldwaarde
xs:dateDatum (JJJJ-MM-DD).2026-03-04
xs:timeTijd in de notatie hh:mm:ss).14:30:00
xs:dateTimeCombinatie van datum en tijd.2026-03-04T14:30:00
xs:gYearAlleen een jaar2026
xs:durationTijdsduur (PnYnMnDTnHnMnS).P1Y2M3D (1 jaar, 2 maanden, 3 dagen)
P1Y2M3DT2H13M30S (1 jaar, 2 maanden, 3 dagen), 2 uur, 13 minuten en 30 seconden).

Overige typen:

TypeBeschrijvingVoorbeeldwaarde
xs:booleanWaar of onwaartrue, false, 1, 0
xs:anyURIEen URI/URL/URNhttp://www.example.nl

Let op: Het kiezen van het juiste datatype is belangrijk. Als je xs:string gebruikt voor een geboortedatum, accepteert het schema elke tekst — ook “gisteren” of “binnenkort”. Met xs:date dwing je het formaat JJJJ-MM-DD af.

Oefening 5.3.1

Naar de oefening.

Simpel vs. complex

Een element kan van een complex of simpel type zijn. Elementen van het simpele type bevatten alleen een waarde (tekst, getal, datum) maar geen kind-elementen en attributen. Elementen van het complexe type kunnen naast een waarde wel kind-elementen en/of attributen bevatten.

In de volgende paragrafen bouwen we voort op dit inzicht door het beperken van simpele en complexe types of het uitbreiden van complexe types m.b.v. een <xs:simpleType> of een <xs:complexType>.

Simpele typen met restricties

Vaak wil je de toegestane waarden verder beperken. Bijvoorbeeld: een postcode moet precies 4 cijfers gevolgd door 2 letters zijn. Daarvoor gebruik je dan een facet binnen een <xs:restriction>, hieronder een voorbeeld met de facet <xs:pattern>:

<xs:element name="Postcode">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:pattern value="[0-9]{4}[A-Z]{2}"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>

Enkele beschikbare facets:

FacetWerkt opBetekenisVoorbeeld
xs:minLengthTekstMinimaal aantal tekens.<xs:minLength value="1"/>
xs:maxLengthTekstMaximaal aantal tekens.<xs:maxLength value="100"/>
xs:lengthTekstExact aantal tekens.<xs:length value="6"/>
xs:patternTekstMoet voldoen aan een reguliere expressie.<xs:pattern value="[0-9]{4}[A-Z]{2}"/>
xs:enumerationAlle typenWaarde moet uit een hierin gedefinieerde lijst van waarden komen.<xs:enumeration value="M"/>
xs:minExclusiveGetallen/datumsMinimale waarde (exclusief)<xs:minExclusive value="8.23"/>
xs:maxInclusiveGetallen/datumsMaximale waarde (inclusief)<xs:maxInclusive value="2023-01-01"/>
xs:totalDigitsGetallenMaximaal aantal cijfers totaal<xs:totalDigits value="5"/>
xs:fractionDigitsDecimalenMaximaal aantal decimalen<xs:fractionDigits value="2"/>

Voorbeeld: enumeratie (vaste keuzelijst)

<xs:element name="Geslacht">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:enumeration value="M"/>
      <xs:enumeration value="V"/>
      <xs:enumeration value="O"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>

Binnen het element <Geslacht> zijn alleen de waaden M, V of O toegestaan. Elke andere waarde is ongeldig.

Voorbeeld: patroon (reguliere expressie)

<xs:element name="BSN">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:pattern value="[1-9]\d{8}"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>

Definieert dat het element <BSN> uit 9 cijfers moet bestaan waarvan het eerste cijfer niet de waarde ‘0’ mag hebben.

Restricties combineren:

<xs:element name="Postcode">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:length value="6"/>
      <xs:pattern value="[0-9]{4}[A-Z]{2}"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>

Definieert dat het element <Postcode> uit 4 cijfers bestaat gevolgd door 2 hoofdletters. Daarnaast mag het niet uit meer dan 6 karakters bestaan. In feite is de facet <xs:length> hier overbodig aangezien het facet <xs:pattern> al definieert dat de maximale lengte 6 karakters is.

StUF-context: In StUF-schema’s worden restricties veelvuldig gebruikt. BSN’s moeten precies 9 cijfers zijn, postcodes hebben een vast patroon, en geslachtsaanduidingen komen uit een vaste lijst.

Oefening 5.3.2

Naar de oefening.

Complexe typen: elementen met structuur

In de praktijk bevatten de meeste elementen kind-elementen en/of attributen. Om dat te kunnen definiëren heb je een <xs:complexType> nodig.

Hieronder enkele voorbeelden:

  • Een element met één of meer kind-elementen:
    <xs:element name="persoon">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="voornaam" type="xs:string"/>
          <xs:element name="achternaam" type="xs:string"/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>
    
  • Een element met kind-elementen en attributen:
    <xs:element name="kostprijs">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="btw" type="xs:positiveInteger"/>
          <xs:element name="prijs" type="xs:nonNegativeInteger"/>
          <xs:element name="totaal" type="xs:nonNegativeInteger"/>
        </xs:sequence>
        <xs:attribute name="valuta" type="xs:string"/>
      </xs:complexType>
    </xs:element>
    
  • Een element met een waarde en een attribuut:
    <xs:element name="bedrag">
      <xs:complexType>
        <xs:simpleContent>
          <xs:extension base="xs:decimal">
            <xs:attribute name="valuta" type="xs:string"/>
          </xs:extension>
        </xs:simpleContent>
      </xs:complexType>
    </xs:element>
    
  • Een element met alleen een attribuut:
    <xs:element name="klantRef">
      <xs:complexType>
        <xs:attribute name="id" type="xs:string"/>
      </xs:complexType>
    </xs:element>
    
    Het volgende XML fragment voldoet daaraan:
    <bedrag valuta="EUR">1500.00</bedrag>
    

Het volgende valt hier op:

  • Voor het toekennen van kind-elementen is een <xs:sequence> nodig. Dit element is een van de drie te gebruiken compositors. In de volgende paragraaf gaan we hier iets dieper op in;
  • De definitie van een attribuut vindt direct plaats binnen een <xs:complexType> element behalve als het element zelf alleen tekstuele content (xs:sring, xs:integer, etc…) kan bevatten. In dat geval wordt een <xs:simpleContent> element geplaatst in het <xs:complexType> element.

Compositors: de volgorde van kind-elementen

XML-Schema kent drie compositors die beschrijven hoe kind-elementen zich tot elkaar verhouden. We benoemen ze hieronder en illustreren ze met een voorbeeld:

xs:sequence

De elementen moeten in de gedefinieerde volgorde worden geplaatst.

<xs:complexType name="PersoonType">
  <xs:sequence>
    <xs:element name="voornaam" type="xs:string"/>
    <xs:element name="achternaam" type="xs:string"/>
    <xs:element name="geboortedatum" type="xs:date"/>
  </xs:sequence>
</xs:complexType>

StUF-context: StUF-schema’s gebruiken vrijwel uitsluitend xs:sequence. De volgorde van elementen in StUF-berichten is altijd vast.

xs:choice

Er moet een keuze uit de gedefinieerde elementen worden gemaakt. Later zullen we zien dat er mechanismes zijn waarmee we deze keuze meerdere keren achter elkaar kunnen maken.

<xs:complexType name="ContactType">
  <xs:choice>
    <xs:element name="email" type="xs:string"/>
    <xs:element name="telefoon" type="xs:string"/>
    <xs:element name="postadres" type="xs:string"/>
  </xs:choice>
</xs:complexType>

xs:all

Alle elementen mogen in een willekeurige volgorde worden geplaatst. Ze kunnen echter maar één keer geplaatst worden maar mogen ook achterwege blijven.

<xs:complexType name="AdresType">
  <xs:all>
    <xs:element name="straat" type="xs:string"/>
    <xs:element name="huisnummer" type="xs:positiveInteger"/>
    <xs:element name="postcode" type="xs:string"/>
  </xs:all>
</xs:complexType>

Oefening 3

Naar de oefening.

Lokaal vs globaal

Tot nu toe heeft het wijzigen van de simpele en complexe types steeds lokaal plaats gevonden. Daarmee bedoelen we dat deze binnen het <xs:element> element heeft plaatsgevonden. In oefening 3 heb je echter vast wel opgemerkt dat het schema niet heel efficient in elkaar zit. Verschillende onderdelen hebben we tijdens het vervaardigen van het XML-Schema gekopieerd naar andere elementdefinities, bijv. de klantgegevens. Als we nu een wijziging aan moeten brengen in die klantgegevens dan moeten we dat op 3 plaatsen doen. Dit kan leiden tot fouten of onzorgvuldigheden. Je zal vast bedacht hebben dat dat beter moet kunnen en ja, gelukkig biedt XML-Schema daar voorzieningen voor. In dit geval is het handiger het type globaal te definiëren en er vanuit de elementen naar te verwijzen.

Daartoe definieer je direct binnen het <xs:schema> element een <xs:simpleType> danwel <xs:complexType> met daarin een name attribuut. Het <xs:element> element waarvan je wil dat het dit moet gaan gebruiken i.p.v. een <xs:simpleType> danwel <xs:complexType> een type attribuut met als waarde een verwijzing naar dat <xs:simpleType> danwel <xs:complexType> element.

Hieronder een voorbeeld met een globaal gedefinieerde <xs:complexType.:

<xs:complexType name="PersoonType">
  <xs:sequence>
    <xs:element name="voornaam" type="xs:string"/>
    <xs:element name="achternaam" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

<xs:element name="persoon" type="PersoonType"/>
<xs:element name="contactpersoon" type="PersoonType"/>

Let op! Tot nu toe hebben we steeds XML-Schema fragmenten gecreëerd die niet aan een namespace zijn gekoppeld. Later zulen we zien dat je in een XML-Schema aan kunt geven op welke namespace dat XML-Schema betrekking heeft. Dat heeft direct gevolgen voor de wijze waarop je in een type attribuut verwijst naar een globaal gedefinieerde <xs:simpleType> danwel <xs:complexType>.

Oefening 4

Naar de oefening.

Nillable: expliciet “geen waarde”

Soms moet een element aanwezig zijn, maar hoeft het geen waarde te hebben. In XSD:

<xs:element name="overlijdensdatum" type="xs:date" nillable="true"/>

In het XML-document:

<overlijdensdatum xsi:nil="true"/>

StUF-context: Het nillable-mechanisme wordt in StUF gebruikt om aan te geven dat een gegeven bewust niet gevuld is. Een leeg element kan “nog niet ingevuld” betekenen, terwijl xsi:nil="true" betekent “er is vastgesteld dat er geen waarde is.”


Kardinaliteit: minOccurs en maxOccurs

Kardinaliteit bepaalt hoe vaak een element mag (of moet) voorkomen:

AttribuutBetekenisStandaard
minOccursMinimaal aantal keer1
maxOccursMaximaal aantal keer1

Veelgebruikte combinaties:

CombinatieBetekenis
(standaard)Verplicht, precies 1 keer
minOccurs="0"Optioneel (0 of 1 keer)
minOccurs="0" maxOccurs="unbounded"0 of meer keer
minOccurs="1" maxOccurs="unbounded"1 of meer keer

Voorbeeld:

<xs:complexType name="PersoonType">
  <xs:sequence>
    <xs:element name="voornaam" type="xs:string"/>                          <!-- Verplicht, 1x -->
    <xs:element name="tussenvoegsel" type="xs:string" minOccurs="0"/>       <!-- Optioneel -->
    <xs:element name="achternaam" type="xs:string"/>                        <!-- Verplicht, 1x -->
    <xs:element name="telefoonnummer" type="xs:string"
                minOccurs="0" maxOccurs="unbounded"/>                       <!-- 0 of meer -->
    <xs:element name="adres" type="AdresType"
                minOccurs="1" maxOccurs="3"/>                               <!-- 1 tot 3 -->
  </xs:sequence>
</xs:complexType>

StUF-context: In StUF-schema’s zijn veel elementen optioneel (minOccurs="0") omdat niet elk bericht alle gegevens bevat. Het begrijpen van minOccurs en maxOccurs is essentieel voor het lezen van StUF-schema’s.

HIER IETS ZEGGEN OVER COMPOSITORS EN Kardinaliteit Bij xs:all mag de maxOccurs bijv. niet hoger zijn dan 1.

Attributen definiëren in XSD

Attributen definieer je met <xs:attribute>, na de compositor:

<xs:complexType name="AdresType">
  <xs:sequence>
    <xs:element name="straat" type="xs:string"/>
    <xs:element name="huisnummer" type="xs:positiveInteger"/>
  </xs:sequence>
  <xs:attribute name="type" type="xs:string" use="required"/>
</xs:complexType>
use-waardeBetekenis
optionalHet attribuut is optioneel (standaard)
requiredHet attribuut is verplicht
prohibitedHet attribuut mag niet voorkomen (gebruikt bij overerving)

Attributen met een eigen restrictie-type:

<xs:simpleType name="MutatiesoortType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="T"/>
    <xs:enumeration value="W"/>
    <xs:enumeration value="V"/>
  </xs:restriction>
</xs:simpleType>

<xs:attribute name="mutatiesoort" type="MutatiesoortType"/>

StUF-context: Het attribuut mutatiesoort is een van de meest kenmerkende attributen in StUF-berichten. Het geeft aan wat voor soort wijziging het bericht bevat (Toevoeging, Wijziging, Verwijdering, etc.).

Geneste vs. globale definities

Gebruik globaal wanneer…Gebruik lokaal wanneer…
Het type op meerdere plekken gebruikt wordtHet type maar op één plek gebruikt wordt
Andere schema’s ernaar moeten kunnen verwijzenHet type specifiek is voor dit ene element
Je een duidelijk overzicht wilt van alle typenJe het schema compact wilt houden

StUF-context: StUF-schema’s werken voornamelijk met globale definities. De typen voor personen, adressen, zaken worden centraal gedefinieerd en vervolgens hergebruikt in meerdere berichtdefinities.


Schema’s opsplitsen: xs:include en xs:import

Een groot schema in één bestand wordt al snel onoverzichtelijk. XSD biedt twee mechanismen om schema’s over meerdere bestanden te verdelen.

xs:include — bestanden samenvoegen (zelfde namespace):

<!-- persoon.xsd -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.example.nl/schema">

  <xs:include schemaLocation="typen-basis.xsd"/>

  <xs:complexType name="PersoonType">
    <xs:sequence>
      <xs:element name="naam" type="xs:string"/>
      <xs:element name="adres" type="tns:AdresType"/>  <!-- Uit typen-basis.xsd -->
    </xs:sequence>
  </xs:complexType>

</xs:schema>

xs:import — schema’s uit een andere namespace:

<!-- persoon.xsd (namespace: http://www.example.nl/persoon) -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.example.nl/persoon"
           xmlns:adr="http://www.example.nl/adres">

  <xs:import namespace="http://www.example.nl/adres"
             schemaLocation="adres.xsd"/>

  <xs:complexType name="PersoonType">
    <xs:sequence>
      <xs:element name="naam" type="xs:string"/>
      <xs:element name="adres" type="adr:AdresType"/>  <!-- Uit adres.xsd -->
    </xs:sequence>
  </xs:complexType>

</xs:schema>
Kenmerkxs:includexs:import
NamespaceZelfde of geenAndere
Attribuut namespaceNiet nodigVerplicht
AnalogieBestanden samenvoegenBibliotheek importeren
GebruikOpknippen van groot schemaHergebruik van extern schema

StUF-context: StUF-schema’s zijn zeer modulair. Het basisschema stuf0302.xsd definieert fundamentele StUF-typen, en domeinschema’s importeren deze om hun eigen berichttypes te definiëren. Je zult tientallen import- en include-regels tegenkomen.

Type-afleiding: extension en restriction

Een van de krachtigste features van XSD is type-afleiding: een nieuw type baseren op een bestaand type, vergelijkbaar met overerving in objectgeoriënteerd programmeren.

xs:extension — een type uitbreiden:

<!-- Basistype -->
<xs:complexType name="PersoonType">
  <xs:sequence>
    <xs:element name="naam" type="xs:string"/>
    <xs:element name="geboortedatum" type="xs:date"/>
  </xs:sequence>
</xs:complexType>

<!-- Afgeleid type: voegt elementen toe -->
<xs:complexType name="MedewerkerType">
  <xs:complexContent>
    <xs:extension base="PersoonType">
      <xs:sequence>
        <xs:element name="functie" type="xs:string"/>
        <xs:element name="afdeling" type="xs:string"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

MedewerkerType bevat nu alle elementen van PersoonType plus de extra elementen:

PersoonType          (basistype)
  ├── naam
  └── geboortedatum
MedewerkerType       (afgeleid = extends PersoonType)
  ├── naam            ← geërfd
  ├── geboortedatum   ← geërfd
  ├── functie         ← toegevoegd
  └── afdeling        ← toegevoegd

xs:restriction — een type beperken:

<!-- Basistype: alles optioneel -->
<xs:complexType name="PersoonType">
  <xs:sequence>
    <xs:element name="naam" type="xs:string"/>
    <xs:element name="geboortedatum" type="xs:date" minOccurs="0"/>
    <xs:element name="adres" type="xs:string" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>

<!-- Restrictietype: naam en geboortedatum verplicht, geen adres -->
<xs:complexType name="KernPersoonType">
  <xs:complexContent>
    <xs:restriction base="PersoonType">
      <xs:sequence>
        <xs:element name="naam" type="xs:string"/>
        <xs:element name="geboortedatum" type="xs:date"/>
      </xs:sequence>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

StUF-context: StUF maakt intensief gebruik van xs:restriction. Het basistype bevat alle denkbare elementen (vaak optioneel), en per berichttype wordt een restriction gemaakt die alleen de relevante elementen overhoudt. Dit is het zogenaamde “Russische poppetjes-model” (matryoshka-model) van StUF.

TechniekWat doet het?Richting
xs:extensionVoegt elementen/attributen toeBasistype → ruimer
xs:restrictionBeperkt of verwijdert optionele delenBasistype → strikter

Abstracte typen en substitutiegroepen

Een type kan gemarkeerd worden als abstract — het mag niet direct in XML-documenten gebruikt worden:

<xs:complexType name="VoertuigType" abstract="true">
  <xs:sequence>
    <xs:element name="kenteken" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="AutoType">
  <xs:complexContent>
    <xs:extension base="VoertuigType">
      <xs:sequence>
        <xs:element name="aantalDeuren" type="xs:integer"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

In XML moet je met xsi:type aangeven welk concreet type je bedoelt:

<voertuig xsi:type="AutoType">
  <kenteken>AB-123-CD</kenteken>
  <aantalDeuren>5</aantalDeuren>
</voertuig>

Groepen: xs:group en xs:attributeGroup

xs:group — herbruikbare elementgroep:

<xs:group name="NaamGroep">
  <xs:sequence>
    <xs:element name="voornaam" type="xs:string"/>
    <xs:element name="tussenvoegsel" type="xs:string" minOccurs="0"/>
    <xs:element name="achternaam" type="xs:string"/>
  </xs:sequence>
</xs:group>

<xs:complexType name="PersoonType">
  <xs:sequence>
    <xs:group ref="NaamGroep"/>
    <xs:element name="geboortedatum" type="xs:date"/>
  </xs:sequence>
</xs:complexType>

xs:attributeGroup — herbruikbare attribuutgroep:

<xs:attributeGroup name="StUFMetadata">
  <xs:attribute name="mutatiesoort" type="xs:string"/>
  <xs:attribute name="indicatorOvername" type="xs:string"/>
  <xs:attribute name="noValue" type="xs:string"/>
</xs:attributeGroup>

<xs:complexType name="PersoonType">
  <xs:sequence>
    <xs:element name="naam" type="xs:string"/>
  </xs:sequence>
  <xs:attributeGroup ref="StUFMetadata"/>
</xs:complexType>

StUF-context: xs:attributeGroup wordt in StUF-schema’s veelvuldig gebruikt. De basismetadata-attributen die bij elk StUF-element horen (zoals mutatiesoort, noValue) worden als attributeGroup gedefinieerd en overal hergebruikt.

Modulaire schema-architectuur

In echte projecten (en zeker in StUF) zie je een gelaagde opzet:

basis-typen.xsd
├── Simpele typen (Postcode, BSN, Datum, etc.)
└── Attribuutgroepen (metadata)
domein-typen.xsd
├── include basis-typen.xsd
├── Complexe typen (PersoonType, AdresType, etc.)
└── Elementgroepen (NaamGroep, etc.)
berichten.xsd
├── include domein-typen.xsd
├── Berichttypen (met restriction op domeintypen)
└── Root-elementen

In StUF concreet:

stuf0302.xsd                 ← Basis StUF-types en attributen
    ├── import ──→ bg0310_stuf_simpleTypes.xsd
    ├── import ──→ bg0310_ent_basis.xsd      ← Entiteittypen (persoon, adres, etc.)
    │                 │
    │                 └── import ──→ gml.xsd  ← Geometrie
    └── bg0310_msg_vraagAntwoord.xsd         ← Berichtstructuren

Namespaces in XSD

In sectie 5.2 leer je wat XML-namespaces zijn. Hier zie je hoe namespaces werken aan de schema-kant.

targetNamespace — de namespace van je schema:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.example.nl/persoon"
           xmlns:per="http://www.example.nl/persoon">

  <xs:element name="persoon" type="per:PersoonType"/>

  <xs:complexType name="PersoonType">
    <xs:sequence>
      <xs:element name="naam" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

Alle globale definities (persoon, PersoonType) behoren tot de targetNamespace. Als je ernaar verwijst, heb je de prefix nodig: type="per:PersoonType".

elementFormDefault — bepaalt of lokale elementen een namespace-prefix nodig hebben:

WaardeBetekenis
unqualified (standaard)Lokale elementen hebben geen prefix in XML
qualifiedLokale elementen moeten in de namespace staan

StUF-context: StUF-schema’s gebruiken elementFormDefault="qualified". In de praktijk wordt vaak de default namespace gebruikt zodat niet alle elementen een prefix nodig hebben.

Schema koppelen aan een XML-document:

Met xsi:schemaLocation (paren van namespace + bestandslocatie):

<persoon xmlns="http://www.example.nl/persoon"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.example.nl/persoon persoon.xsd">
  <naam>Jan</naam>
</persoon>

Met meerdere schema’s:

<bericht xmlns="http://www.example.nl/bericht"
         xmlns:per="http://www.example.nl/persoon"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="
           http://www.example.nl/bericht  bericht.xsd
           http://www.example.nl/persoon  persoon.xsd">
  <!-- ... -->
</bericht>

Let op: xsi:schemaLocation is een hint voor de validator, geen verplichting. De validator mág een ander schema gebruiken.


Compleet voorbeeld: alles samen

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

  <!-- Simpele typen met restricties -->
  <xs:simpleType name="BSN">
    <xs:restriction base="xs:string">
      <xs:pattern value="[0-9]{9}"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="Postcode">
    <xs:restriction base="xs:string">
      <xs:pattern value="[0-9]{4}[A-Z]{2}"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="Geslacht">
    <xs:restriction base="xs:string">
      <xs:enumeration value="M"/>
      <xs:enumeration value="V"/>
      <xs:enumeration value="O"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- Complexe typen -->
  <xs:complexType name="AdresType">
    <xs:sequence>
      <xs:element name="straat" type="xs:string"/>
      <xs:element name="huisnummer" type="xs:positiveInteger"/>
      <xs:element name="huisletter" type="xs:string" minOccurs="0"/>
      <xs:element name="postcode" type="Postcode"/>
      <xs:element name="woonplaats" type="xs:string"/>
    </xs:sequence>
    <xs:attribute name="type" type="xs:string" use="required"/>
  </xs:complexType>

  <xs:complexType name="PersoonType">
    <xs:sequence>
      <xs:element name="voornaam" type="xs:string" maxOccurs="unbounded"/>
      <xs:element name="tussenvoegsel" type="xs:string" minOccurs="0"/>
      <xs:element name="achternaam" type="xs:string"/>
      <xs:element name="geslacht" type="Geslacht"/>
      <xs:element name="geboortedatum" type="xs:date"/>
      <xs:element name="overlijdensdatum" type="xs:date" nillable="true" minOccurs="0"/>
      <xs:element name="adres" type="AdresType" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="bsn" type="BSN" use="required"/>
  </xs:complexType>

  <!-- Root-element -->
  <xs:element name="persoon" type="PersoonType"/>

</xs:schema>

Een geldig XML-document:

<persoon bsn="123456789">
  <voornaam>Jan</voornaam>
  <voornaam>Pieter</voornaam>
  <tussenvoegsel>de</tussenvoegsel>
  <achternaam>Vries</achternaam>
  <geslacht>M</geslacht>
  <geboortedatum>1985-03-15</geboortedatum>
  <overlijdensdatum xsi:nil="true"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
  <adres type="woonadres">
    <straat>Kerkstraat</straat>
    <huisnummer>12</huisnummer>
    <postcode>3511AB</postcode>
    <woonplaats>Utrecht</woonplaats>
  </adres>
</persoon>

In dit ene voorbeeld komen alle behandelde concepten samen: simpele typen met restricties (BSN, Postcode, Geslacht), complexe typen (AdresType, PersoonType), kardinaliteit (minOccurs, maxOccurs), verplichte attributen (use="required"), nillable elementen, en de boomstructuur van geneste elementen.

Samenvatting

ConceptUitleg
XSDXML-Schema Definition — beschrijft de toegestane structuur van XML in XML-formaat
Welgevormd vs. geldigWelgevormd = syntax OK; geldig = voldoet aan schema
Ingebouwde datatypenxs:string, xs:integer, xs:date, xs:boolean, etc.
Restricties (facets)pattern, enumeration, minLength, maxInclusive, etc.
Compositorsxs:sequence (volgorde), xs:choice (keuze), xs:all (vrij)
KardinaliteitminOccurs / maxOccurs — hoe vaak een element mag voorkomen
xs:include / xs:importSchema’s samenvoegen (zelfde ns) / importeren (andere ns)
Extension / restrictionType uitbreiden of beperken (overerving)
xs:group / xs:attributeGroupHerbruikbare groepen elementen of attributen
targetNamespaceNamespace waaraan het schema zijn definities toekent
elementFormDefaultBepaalt of lokale elementen gekwalificeerd moeten zijn
xsi:schemaLocationKoppelt namespace aan schema-bestand in XML

Kernpunt: XSD is een taal (zelf ook XML) waarmee je exact vastlegt hoe een XML-document eruit moet zien. StUF is volledig gedefinieerd in XSD-schema’s — het begrijpen van XSD is daarom essentieel voor het werken met StUF. De belangrijkste concepten zijn: datatypen met restricties, compositors voor structuur, kardinaliteit voor optionaliteit, en modulaire schema-opzet met include/import en type-afleiding.

Tot slot

Zoals gezegd is XML-Schema een standaard van het W3C. Een volledige specificatie van de standaard vind je dan ook op hun site: