XML structuren van elkaar onderscheiden
Door Michiel van Otegem
18 maart 2007
Een van de grootste voordelen van XML is dat je zelf elementen en attributen kunt bedenken. Met
andere woorden, je kunt zelf een XML vocabulaire specificeren. Daar zit echter wel een keerzijde
aan, want hoe weet een applicatie welke vocabulaire een document gebruikt, en wat gebeurt er als
twee vocabulaires hetzelfde element bevatten?
Als je gebruik gaat maken van XML om gegevens te beschrijven, kun je de structuur, elementnamen en
attribuutnamen zelf bepalen. Het ligt daarbij voor de hand dat je namen gebruikt die inzicht geven
in de betekenis van een waarde. Voor een document met fotomodellen zou je bijvoorbeeld de XML in
afbeelding 1 kunnen gebruiken. Voor schaalmodellen van vliegtuigen zou je (bijna) identieke XML
kunnen gebruiken, zoals te zien in afbeelding 2. De XML in afbeelding 2 bevat een attribuut dat
de XML in afbeelding 1 niet bevat, maar dat attribuut zou best wel eens optioneel kunnen zijn.
Het zou dus onverstandig zijn om te bepalen of de XML die je verwacht klopt op basis van dat
attribuut. Eigenlijk ga je dan ook kijken naar de betekenis van de gegevens om de twee documenten
uit elkaar te houden. Een parser die we gebruiken om een XML document in te lezen heeft echter
geen weet van de betekenis. De parser kijkt alleen maar naar de grammatica en leest de elementen
in als de XML maar aan de grammatica regels voldoet (well-formed is). Daarna geeft de parser het
document aan een applicatie, die dan moet bepalen of het document de juiste betekenis heeft. Het
zou veel handiger zijn als de applicatie er zeker van is dat een document dat het krijgt de juiste
gegevens bevat. Hiervoor moeten we ten eerste kunnen bepalen welke vocabulaire een document
gebruikt, en vervolgens de vocabulaire en structuur definiëren, waamee we dan documenten kunnen
controleren.
1: <modellen>
2: <model>Cindy Crawford</model>
3: <model>Naomi Campbell</model>
4: </modellen>
Afbeelding 1, XML document met fotomodellen
1: <modellen>
2: <model schaal="1:250">Boeing 747-400</model>
3: <model schaal="1:144">Airbus A340-300</model>
4: </modellen>
Afbeelding 2, XML document met schaalmodellen
Vocabulaires scheiden
We kunnen twee verschillende vocabulaires onderscheiden door gebruik te maken van XML Namespaces.
Een namespace (of naamruimte) vertelt een parser tot welke vocabulaire een element of attribuut
behoort. Als twee elementen dezelfde naam hebben, dan behoren ze alleen tot dezelfde vocabulaire
als ze ook tot dezelfde namespace behoren. Tot welke namespace een element behoort doen we met
behulp van een speciaal XML attribuut: xmlns. De waarde van het xmlns attribuut moet een Universal
Resource Identifier (URI) zijn, hetgeen de keuze laat tussen een Universal Resource Name (URN) of
een Universal Resource Locator (URL). De laatste kennen we uiteraard allemaal, want die gebruiken
we om web pagina's op te vragen. Afbeelding 3 laat zien hoe je afbeelding 1 kunt aanpassen om het
te onderscheiden van afbeelding 2. Je ziet dat er een URL wordt gebruikt, en dat deze naar een map
wijst op een bepaalde website. Er zijn geen regels die bepalen waar een URL naar wijst als je die
gebruikt voor een namespace. Het is echter de conventie dat je verwijst naar een map, en dat in
die map zonodig vocabulaire definities en documentatie over de namespace bevat. Deze map kun je
dan voorzien van een zogenaamd Resource Directory Description Language (RDDL) document dat
definiëert welke documenten je kunt vinden in de map. Wat je ook doet, het doel is uiteindelijk
dat een vocabulaire een unieke identificatie heeft, zodat het niet kan gebeuren dat twee
verschillende vocabulaires dezelfde identificatie hebben, want dat is vragen om problemen.
Uiteraard is het wel zo dat het erg handig is dat twee applicaties die dezelfde vocabulaire
gebruiken ook dezelfde namespace gebruiken. Hoewel dit voor de hand ligt, zijn er geen regels
voor het definiëren en bekend maken van een namespace, dus kan het best zijn dat iemand die
precies dezelfde vocabulaire definieert een andere namespace gebruikt. In dat geval is het dus zo
dat hoewel de documenten qua betekenis prima werken, ze als niet gelijk worden beschouwd doordat
ze een verschillende namespace gebruiken.
1: <modellen xmlns="http://www.voorbeeld.nl/schema/fotomodellen">
2: <model>Cindy Crawford</model>
3: <model>Naomi Campbell</model>
4: </modellen>
Afbeelding 3, XML document met een namespace
Met de namespace koppeling in afbeelding 3 zijn we er nog niet, want stel je voor dat een document
elementen uit verschillende vocabulaires combineert. Om dit te kunnen verzorgen, kun je elementen
voorzien van een zogenaamde namespace prefix, een voorvoegsel waarmee je aangeeft bij welke
namespace definitie een element hoort. Hoe dit werkt zie je in afbeelding 4.
1: <shop:stock xmlns:shop="http://www.voorbeeld.nl/schema/shop">
2: <p:product p:ID="456" p:description="Stekkerdoos met 6 aansluitingen"
3: shop:price="4.95" shop:stock="7"
4: xmlns:p="http:// www.voorbeeld.nl/schema/products" />
5: <p:product p:ID="457" p:description="Stekkerdoos met 4 aansluitingen"
6: shop:price="3.95" shop:stock="3" />
7: </shop:stock>
Afbeelding 4, XML document met meerdere namespaces
Afbeelding 4 onderscheidt twee namespaces. Ze zijn gedefinieerd door het xmlns attribuut gevolgd
door een dubbelepunt en de prefix die binnen het document voor de betreffende namespace gebruikt
wordt. Zo zijn gegevens van producten en gegevens voor het magazijn van de winkel logisch van
elkaar gescheiden. De elementen en attributen gebruiken de prefix die gedefinieerd is voor elk
van de namespaces. Belangrijk hierbij is op te merken dat de prefix alleen binnen het document
van belang is, en alleen de koppeling verzorgt naar de bijbehorende namespace. Je kunt dus een
andere prefix gebruiken voor dezelfde namespace, of dezelfde prefix voor een andere namespace
(mits dit in een ander document is uiteraard). Afbeelding 5 maakt dit duidelijk.
1: <p:modellen xmlns:p="http://www.voorbeeld.nl/schema/fotomodellen">
2: <p:model>Cindy Crawford</p:model>
3: <p:model>Naomi Campbell</p:model>
4: </p:modellen>
Afbeelding 5, XML document met een namespace
De prefix p die gebruikt werd in afbeelding 4, wordt ook gebruikt in afbeelding 5. Ze zijn echter
gekoppeld aan verschillende namespaces, dus de vocabulaire waartoe de elementen behoren zijn
verschillend. Verwarring tussen afbeelding 4 en 5 is er dus niet, terwijl afbeelding 3 en 5 voor
een XML parser volkomen identiek zijn! In afbeelding 3 is geen gebruik gemaakt van een prefix,
waardoor alle elementen en attributen zonder prefix automatisch tot de gedefinieerde namespace
behoren. We noemen die prefix-loze namespace ook wel de default namespace. Een XML parser koppelt de
namespace aan de elementen en attributen, en laat de gebruikte prefix buiten beschouwing, zodat
de uiteindelijke betekenis van afbeelding 3 en 5 gelijk is.
Zoals je kunt zien in de afbeeldingen, wordt een namespace maar één keer gedefinieerd in een
document. Een namespace definitie kan echter niet zo maar op iedere plaats in een XML document
gezet worden. De regel is dat de definitie in het eerste of buitenste element staat dat er gebruik
van maakt. Dit is duidelijk in afbeelding 4, waar de shop prefix aan een namespace gekoppeld wordt
in het root-element van het document, en de p prefix gekoppeld wordt bij het eerste product element.
Als die laatste definitie bij het tweede product gestaan had, was het document incorrect geweest.
Je kunt een namespace overigens ook hoger in de hierarchie definiëren, waardoor je ervoor kunt kiezen
om alle namespaces te definiëren in het root-element. Op die manier weet je onmiddellijk welke
namespaces een document gebruikt, en weet je ook altijd waar je eventuele referenties moet vinden.
Een vocabulaire definiëren
Als je gebruik maakt van namespaces, hoef je de vocabulaire die je gebruikt niet per se verder te
specificeren, zolang voor de betrokkenen bekend is wat de betekenis van een namespace en de
elementen die daarin gebruikt worden is er niets aan de hand. Het voordeel hiervan is dat je
willekeurig elementen kunt toevoegen aan je documenten, zonder dat daardoor een applicatie
problemen krijgt. Het nadeel is echter dat documenten niet gecontroleerd worden op structuur en
vocabulaire. Het is daarom vaak beter om de structuur en de vocabulaire vast te leggen. Je kunt dit
doen met een Document Type Definition (DTD) of een XML Schema. DTDs bestaan al jaren, en worden
gebruikt voor verschillende soorten document definities, niet alleen voor XML. XML Schema
daarentegen is specifiek bedoeld voor XML, en heeft daardoor meer mogelijkheden. Bovendien is een
XML Schema in tegenstelling tot zelf ook XML. Je kunt dus een XML parser gebruiken om een XML
Schema in te lezen en te gebruiken. Omdat XML Schema een eigen namespace gebruikt, kun je het
schema zelfs in een document zetten, zonder dat het interfereert met de gegevens in dat document.
Dit gebeurt bijvoorbeeld wel in applicaties die formuliergedreven zijn, zoals work flow applicaties.
Daarin wordt een XML document gecombineerd met het schema verstuurd, zodat een invoerformulier
zich altijd houdt aan de definitie die voor het betreffende formulier geldt. Verandert de definitie,
dan verandert dat alleen voor nieuwe documenten die gebruik maken van die definitie.
Door gebruik te maken van een schema of DTD weet je altijd dat een document geldig (valid) is, en
kom je in een applicatie die de XML gebruikt niet voor onverwachtte verrassingen te staan. Je weet
namelijk zeker dat de structuur en de vocabulaire precies zo zijn als je verwacht. Omdar de XML
parser aan de hand van een schema of DTD de controle uitvoert, hoef je dit in de applicatie niet
meer te doen. Bovendien gebeurt het op een standaard manier, en hoef je als ontwikkelaar dus geen
extra code te schrijven om allerlei controles uit te voeren.
Tenslotte
XML is door z'n vrijheid heel flexibel, maar dat heeft ook z'n keerzijde. XML Namespaces lost een
gedeelte van dit probleem op door verschillende structuren en vocabulaires van elkaar te scheiden.
XML Schema, dat verder wordt besproken in het volgende deel van deze serie, complementeert XML
Namespaces voor het vastleggen van de structuur en de vocabulaire. XML Namespaces en XML Schema
zijn dermate krachtig en eenvoudig in gebruik dat ze veel last van de schouders van ontwikkelaars
halen.
Dit artikel is eerder verschenen in NetOpus, april 2004.
|