Definició d'esquemes i vocabularis en XML

L’XML permet crear els llenguatges de marques que vulguem, sigui quin sigui el camp d’actuació, ja que en deixar crear les etiquetes que calguin es pot adaptar a qualsevol situació. Aquest és el punt fort de l’XML sobre altres llenguatges de marques: s’adapta al que es vulgui representar sense importar la complexitat que pugui tenir.

Però les dades dels documents XML normalment hauran de ser processades per un programa d’ordinador, i els programes d’ordinador no donen tanta llibertat com l’XML. Aquests no són gaire bons interpretant i entenent informació per a la qual no han estat programats per processar.

Suposem que s’ha desenvolupat un programa per representar imatges a partir de les etiquetes <dibuix>, <rectangle>, <cercle>. Des d’un punt de vista de la llibertat que deixa l’XML no hi haurà cap problema per crear un arxiu XML com aquest:

  1. <dibuix>
  2. <rectangle>12,10,14,10</rectangle>
  3. <linia>13,13,25,25</linia>
  4. </dibuix>

El document és perfectament correcte des d’un punt de vista de l’XML però el programa no sabrà què fer amb l’etiqueta <linia> perquè ningú no l’ha programat. És per aquest motiu que els programes normalment es dissenyen per processar només tipus concrets d’XML.

Per tant, una de les coses que haurà de fer un programa és comprovar que les dades del document XML siguin correctes. Com que aquesta tasca és molt feixuga s’han definit sistemes per comprovar que el document XML entrat conté les etiquetes que ha de contenir i que estan col·locades tal com fa falta. El procés de fer aquestes comprovacions s’anomena validació.

Validació de documents XML

El procés de comprovar que uns fitxers XML determinats segueixen un determinat vocabulari s’anomena validació i els documents XML que segueixen les regles del vocabulari s’anomenen documents vàlids. Cal tenir clara la diferència entre el que és un document ben format i un document vàlid:

Un document és ben format quan segueix les regles d’XML.

Un document és vàlid si segueix les normes del vocabulari que té associat.

Un document pot complir perfectament amb les regles de creació de documents XML i, per tant, estar ben format, però en canvi no seguir les normes del vocabulari i, per tant, no ser vàlid.

Un document pot ser ben format però no vàlid, i en canvi si és vàlid segur que és ben format.

La validació de documents és un procés corrent en llenguatges de marques, ja que no sempre la persona que defineix com ha de ser el document després és la que generi els documents (de vegades els documents arribaran d’altres llocs, es generaran automàticament…).

La validació és el procés de comprovar que un document compleix amb les regles del vocabulari en tots els seus aspectes.

La validació permet assegurar que la informació està exactament en la manera com ha d’estar. Això és especialment important quan es comparteix informació entre sistemes, ja que validar el document és assegurar-se que realment l’estructura de la informació que s’està passant és la correcta. Si es vol fer que dos programes situats en ordinadors diferents col·laborin cal que la informació que es passen l’un a l’altre segueixi l’estructura prefixada per enviar-se missatges.

Per forçar una determinada estructura en un document cal que hi hagi alguna manera de definir aspectes com ara:

  • en quin ordre han d’anar les etiquetes,
  • quines són correctes i quines no,
  • en quin ordre han d’aparèixer,
  • quins atributs s’hi poden posar,
  • quin contingut hi pot haver,
  • etc.

L’XML fa això per mitjà de llenguatges de definició de vocabularis o llenguatges d’esquemes.

Els llenguatges de definició d’esquemes sorgeixen per la necessitat que els documents XML siguin processats per programes (per extreure’n informació, transformar-los en altres coses, etc), ja que els programes no són tan flexibles com l’XML i necessiten processar dades amb estructures de document tancades.

Hi ha molts llenguatges de definició d’esquemes, però els més utilitzats en XML són:

  • Document type definitions (DTD)
  • W3C XML definition language
  • Relax NG
  • Schematron

El procés de validació es fa per mitjà de programes especials anomenats processadors o validadors (en anglès s’anomenen parsers).

Els validadors sovint estan en forma de biblioteques per poder ser incorporats als programes. No tots els processadors poden validar tots els llenguatges de definició de vocabularis i, per tant, s’hauran de triar en funció del llenguatge que fem servir.

Processadors d'XML

Els processadors XML són els encarregats de validar els documents i, per tant, és molt important triar un processador que sigui capaç de validar el llenguatge de validació que fem servir:

  • El fet que la DTD sigui el sistema més antic i que se’n parli en l’especificació d’XML ha fet que la majoria dels processadors XML puguin validar llenguatges definits amb DTD.
  • Amb el pas del temps, XML Schemas s’ha convertit en la manera estàndard de validar vocabularis XML, i altres tecnologies XML en fan servir alguns aspectes. Per tant, la majoria dels processadors també suporten aquest format.
  • Molts processadors a part dels dos més usats també poden fer servir altres llenguatges (en especial Relax NG).

Biblioteques per validar XML

Els documents XML estan pensats per poder ser tractats de manera automàtica, i moltes de les utilitats per validar documents estan en forma de biblioteques. Entre les biblioteques existents per validar XML en podem destacar:

  • libxml2
  • MSXML
  • Apache Xerces2

libxml2

La utilitat libxml de la biblioteca libxml2 del projecte Gnome permet validar documents XML que tinguin associats DTD, XML Schemas, Relax NG o Schematron.

Validar DTD

La manera més senzilla de fer-ho és especificar el paràmetre –valid, que fa que el programa, a més de comprovar que el document XML estigui ben format, sigui vàlid.

Amb el paràmetre –noout no es mostrarà cap missatge si el document és correcte.

  1. $ xmllint --valid prova.xml --noout
  2. $

També ofereix l’opció de validar un document extern amb el paràmetre –dtdvalid. Això és especialment interessant per poder validar sense haver d’associar el document XML i la DTD.

  1. $ xmllint alumnes.xml --dtdvalid alumnes.dtd --noout
  2. $

En cas que el document no validi mostrarà l’error trobat:

  1. $ xmllint --valid prova.xml --noout
  2. prova.xml:13: element classe: validity error :
  3. Element classe content does not follow the DTD,
  4. expecting (classe , professor , alumnes), got (professor alumnes )
  5. </classe>
  6. ^
Validar XML Schemas

Per validar un document amb un XML Schema s’ha d’especificar el document d’esquema fent servir el paràmetre –schema:

  1. xmllint --schema classe.xsd classe.xml --noout
  2. classe.xml validates

Si no valida mostrarà els errors que hi hagi detectat:

  1. $ xmllint --schema classe.xsd classe.xml --noout --valid
  2. classe.xml:3: element curs: Schemas validity error :
  3. Element 'curs': This element is not expected. Expected is ( professor ).
  4. classe.xml fails to validate
Validar amb Relax NG i Schematron

No hi ha massa diferències a l’hora de validar contra Relax NG o Schematron. Simplement s’especifica el paràmetre correcte. Per a Relax NG és –relaxng:

  1. $ xmllint --relaxng classe.rng classe.xml --noout
  2. classe.xml validates

MSXML

Des dels entorns Windows es pot fer servir la biblioteca MSXML per validar els documents XML contra DTD o XML Schema.

Aquesta biblioteca ofereix una interfície per desenvolupar aplicacions que treballin amb diferents tecnologies XML. Es fa servir en molts dels programes de Microsoft i és la que fan servir per defecte els programes basats en la plataforma .NET (C#, Visual Basic, J#, managed C++ …).

Alguns dels editors especialitzats en XML que funcionen exclusivament en Windows fan servir aquesta biblioteca per funcionar.

Apache Xerces2

Aquesta biblioteca està pensada per ser usada des de programes i no des de la línia d’ordres (per exemple, molts dels editors especialitzats en XML fan o permeten fer servir Xerces2 per validar els documents XML). A pesar d’això, sí que és possible validar un document des de la línia d’ordres si s’instal·len els exemples, ja que contenen un programa anomenat Counter que permet fer estadístiques d’un document i que alhora valida.

En una instal·lació d’Ubuntu s’executaria el programa i donaria el resultat següent:

  1. $ java -cp /usr/share/java/xercesImpl.jar:\
  2. >/usr/share/java/xmlParserAPIs.jar:\
  3. >/usr/share/doc/libxerces2-java-doc/examples/xercesSamples.jar \
  4. dom/Counter -v alumnes.xml
  5. [Error] alumnes.xml:7:11: The content of element type "alumne" is incomplete, it must match "(nom,cognom,cognom)".
  6. [Error] alumnes.xml:11:11: The content of element type "alumne" is incomplete, it must match "(nom,cognom,cognom)".
  7. [Error] alumnes.xml:15:11: The content of element type "alumne" is incomplete, it must match "(nom,cognom,cognom)".
  8. alumnes.xml: 229;15;0 ms (10 elems, 9 attrs, 31 spaces, 36 chars)

Si el document valida correctament mostrarà estadístiques del document:

  1. $ java -cp /usr/share/java/xercesImpl.jar:\
  2. >/usr/share/java/xmlParserAPIs.jar:\
  3. >/usr/share/doc/libxerces2-java-doc/examples/xercesSamples.jar \
  4. >dom/Counter -v alumnes.xml
  5. alumnes.xml: 39;7;0 ms (10 elems, 9 attrs, 31 spaces, 36 chars)

En estar desenvolupat en Java també pot ser executat en entorns Windows. Suposant que tinguem Xerces a C:\xerces es pot validar de la mateixa manera:

c:\> java -cp c:\xerces\xercesImpl.jar; 
c:\xerces\xmlParserAPIs.jar; 
c:\xerces\xercesSamples.jar dom/Counter -v alumnes.xml
alumnes.xml: 39;7;0 ms (10 elems, 9 attrs, 31 spaces, 36 chars)

Programes

Tot i que els validadors sobretot es fan servir com a biblioteques incorporades en programes, també hi ha programes que permeten validar documents sense haver d’escriure ni una línia de codi. Exemples d’aquets tipus de programes poden ser XMLStarlet o Jing.

Validar amb XMLStarlet

L’XMLStarlet és un programa de codi obert que s’executa des de línia d’ordres i que pot manipular fitxers XML. Fa servir libxml2 i es pot baixar des de http://xmlstar.sourceforge.net/.

Una de les tasques que s’hi poden fer és validar documents XML definits amb DTD, XML Schemas i Relax NG. Si el document ja té associada la DTD n’hi ha prou d’executar la instrucció amb validate i el nom del fitxer:

  1. $ xmlstarlet validate prova.xml
  2. prova.xml - valid

O es pot especificar un fitxer de definició extern:

  1. $ xmlstarlet validate --dtd prova.dtd prova.xml
  2. prova.xml - valid

Es pot fer el mateix per validar XML Schemas:

  1. $ xmlstarlet validate --xsd prova.xsd prova.xml
  2. prova.xml - valid

El problema que té el programa és que no informa dels errors trobats:

  1. $ xmlstarlet validate --xsd classe.xsd classe.xml
  2. classe.xml - invalid

Hi ha molts programes que permeten validar documents XML sense haver de programar, però el més habitual des d’un punt de vista professional sol ser fer servir algun editor XML especialitzat, com ara oXygen XML Editor, Editix XML Editor, Altova XMLSpy XML editor o Stylus Studio.

Editors XML

Els editors XML, a més de permetre la creació assistida de documents XML, també tenen alguna opció per fer validacions dels documents sense haver d’abandonar l’editor (figura).

Figura Validació des de l’XML Copy Editor

Aquests editors també detecten els problemes de validació automàticament i marquen les línies amb errors (figura).

Figura No cal sortir de l’entorn per veure’n els errors

Sovint també permeten validar documents XML contra gairebé tots els sistemes de validació: DTD, XML Schemas, Relax NG…

Validar amb l'oXygen XML Editor

Com a exemple de validació amb un editor especialitzat XML farem una validació des de l’oXygen XML Editor.

Un cop el document XML està carregat en el programa cal especificar contra quin document s’ha de validar. Això es fa des de l’opció Configure Validation Scenario (figura).

Figura Configurar un escenari de validació amb l’oXygen

En la pantalla següent es defineix una nova validació i es tria el fitxer d’esquema (figura).

Figura Tria del fitxer per fer la validació

El fitxer quedarà associat al document de manera que cada vegada que es validi el document ho farà contra l’esquema triat automàticament (figura).

Figura Validació d’XML amb l’oXygen XML Editor 12

DTD

El DTD (document type definitions) és un llenguatge de definició d’esquemes que ja existia abans de l’aparició d’XML (es feia servir en SGML). En estar pensat per funcionar amb SGML es podia fer servir en molts dels llenguatges de marques que s’hi han basat, com XML o HTML.

Quan es va definir l’XML es va aprofitar per fer una versió simplificada de DTD que en fos el llenguatge d’especificació d’esquemes original. Un avantatge associat era que fer servir una versió de DTD permetria mantenir la compatibilitat amb SGML, i per tant es van referenciar les DTD en l’especificació d’XML (http://www.w3.org/TR/REC-xml/).

L’objectiu principal de les DTD és proveir un mecanisme per validar les estructures dels documents XML i determinar si el document és vàlid o no. Però aquest no serà l’únic avantatge que ens aportaran les DTD, sinó que també els podrem fer servir per compartir informació entre organitzacions, ja que si algú altre té la nostra DTD ens pot enviar informació en el nostre format i amb el programa que hem fet la podrem processar.

Durant molt de temps les DTD van ser el sistema de definir vocabularis més usat en XML però actualment han estat superats per XML Schemas. Tot i això encara és molt usat, sobretot perquè és molt més senzill.

Associar una DTD a un document XML

Per poder validar un document XML cal especificar-li quin és el document d’esquemes que es farà servir. Si el llenguatge que es fa servir és DTD hi ha diverses maneres d’especificar-ho però en general sempre implicarà afegir una declaració DOCTYPE dins el document XML per validar.

El més habitual és afegir la referència a la DTD dins del document XML per mitjà de l’etiqueta especial <!DOCTYPE. Com que la declaració XML és opcional però si n’hi ha ha de ser la primera cosa que aparegui en un document XML, l’etiqueta DOCTYPE haurà d’anar sempre darrere seu.

Per tant, seria incorrecte fer:

  1. <!DOCTYPE ... >
  2. <?xml version="1.0"?>

Però, per altra banda, l’etiqueta no pot aparèixer en cap altre lloc que no sigui just a continuació de la declaració XML, i per tant tampoc no es pot incloure l’etiqueta DOCTYPE un cop hagi començat el document.

  1. <?xml version="1.0"?>
  2. <document>
  3. <!DOCTYPE ...>
  4. </document>

Ni tampoc al final:

  1. <?xml version="1.0" ?>
  2. <document>
  3. </document>
  4. <!DOCTYPE ... >

L’etiqueta DOCTYPE sempre ha d’anar o bé en la primera línia si no hi ha declaració XML, o bé just al darrere:

  1. <?xml version="1.0" ?>
  2. <!DOCTYPE ... >
  3. <document>
  4. </document>

Hi ha dues maneres d’incorporar DTD en un document XML:

  • Declaració interna
  • Declaració externa

Declaració DTD interna

En les declaracions DTD internes les regles de la DTD estan incorporades en el document XML. L’etiqueta DOCTYPE es declara com es veu en la figura.

Figura Declaració DOCTYPE interna

Per exemple, si es copien les línies següents en un document XML anomenat prova.xml:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE classe [
  3. <!ELEMENT classe (professor, alumnes)>
  4. <!ELEMENT professor (#PCDATA)>
  5. <!ELEMENT alumnes (nom*)>
  6. <!ELEMENT nom (#PCDATA)>
  7. ]>
  8. <classe>
  9. <professor>Marcel Puig</professor>
  10. <alumnes>
  11. <nom>Frededic Pi</nom>
  12. </alumnes>
  13. </classe>

Es pot comprovar que el document es valida fent servir la instrucció següent:

  1. $ xmllint --valid prova.xml --noout

Les declaracions de DTD internes no es fan servir gaire perquè tenen una sèrie de problemes que no les fan ideals:

  • En tenir la definició de l’esquema dins del document XML no és fàcil compartir les declaracions amb altres documents.
  • Si es tenen molts documents XML, per canviar lleugerament la declaració s’hi hauran de fer canvis en tots.

És per aquest motiu que és més recomanable tenir la DTD en un document a part i fer servir aquest document DTD per validar tots els XML.

Declaració DTD externa

En comptes d’especificar la DTD en el mateix document es considera una pràctica molt millor definir-la en un fitxer a part.

Per fer una declaració externa també es fa servir l’etiqueta DOCTYPE però el format és lleugerament diferent i preveu dues possibilitats:

  • DTD privada
  • DTD pública
DTD privades

Les definicions de DTD privades són les més corrents, ja que, tot i que es defineixi el vocabulari com a privat, no hi ha res que impedeixi que el fitxer es comparteixi o es publiqui per mitjà d’Internet. La definició d’una DTD privada es fa de la manera que es pot veure en la figura.

Figura Definició d’una DTD privada en un document XML

El camp d’adreça especifica on és i quin nom té el fitxer que conté les regles. Es pot especificar fent servir una URI (uniform resource identifier). Això fa que es pugui definir la DTD per mitjà d’un nom que per definició hauria de ser únic.

En el camp d’adreça es poden definir camins dins la màquina:

  1. <!DOCTYPE classe SYSTEM "C:\dtd\classe.dtd">

URI

Una URI (uniform resource identifier) és una cadena de caràcters que es fa servir ‘er fer referència única a un recurs local, dins d’una xarxa o a Internet.

O fins i tot es pot definir una DTD per mitjà d’una adreça d’Internet:

  1. <!DOCTYPE classe SYSTEM "http://www.ioc.cat/classe.dtd">

Per tant, podríem definir dins d’un fitxer XML una DTD que tingui l’element arrel <alumnes> d’aquesta manera:

  1. <!DOCTYPE alumnes SYSTEM "alumnes.dtd">

Un cop definit només cal crear l’arxiu alumnes.dtd en el lloc adequat amb les regles que defineixen el vocabulari:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!ELEMENT classe (professor, alumnes) >
  3. <!ELEMENT professor (#PCDATA) >
  4. <!ELEMENT alumnes (nom*) >
  5. <!ELEMENT nom (#PCDATA) >
DTD públiques

Les definicions PUBLIC estan reservades per a DTD que estiguin definides per organismes d’estandardització, ja siguin oficials o no. En la definició d’una DTD pública s’afegeix un camp extra que fa d’identificador de l’organisme (figura).

Figura Definició d’una DTD pública en un document XML

La majoria dels camps són idèntics que en definir una DTD privada però en aquest cas s’hi ha afegit un camp més, que és un identificador. L’identificador pot estar en qualsevol format però el més corrent és fer servir el format FPI (formal public identifiers). L’FPI es defineix per mitjà d’un grup de quatre cadenes de caràcters separades pels dobles barres. En cada posició s’especifica:

  "simbol//Nom del responsable de la DTD//Document descrit//Idioma"

en què cada valor es definirà segons la taula.

Taula: Definició d’un FPI
Camp Valors possibles
primer Si l’estàndard no ha estat aprovat hi haurà un ’-', mentre que si ha estat aprovat per un organisme no estàndard tindrem un ’+'. I en canvi, si ha estat aprovat per un organisme d’estàndards, hi haurà una referència.
Nom del responsable Qui és l’organització o la persona responsable de definir l’estàndard.
Document descrit Conté un identificador únic del document descrit.
Idioma El codi ISO de l’idioma en què està escrit el document.

Per exemple, aquesta seria una declaració correcta:

  1. <!DOCTYPE classe PUBLIC "-//IOC//Classe 1.0//CA" "http://www.ioc.cat/classe.dtd">

Un exemple de DTD pública que es fa servir molt és la declaració d’HTML 4.01:

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  2. "http://www.w3.org/TR/html4/strict.dtd">
Regles internes en declaracions externes

Qualsevol de les definicions externes també pot contenir un apartat opcional que permet especificar-hi un subconjunt de regles internes.

  1. <!DOCTYPE nom SYSTEM "fitxer.dtd" [ regles ] >
  2. <!DOCTYPE classe PUBLIC "-//IOC//Classe 1.0//CA" "fitxer.dtd" [ regles ] >

Si no hi ha regles no cal especificar aquest apartat però també es pot deixar en blanc. Per tant, seria correcte definir la DTD d’aquesta manera:

  1. <!DOCTYPE alumnes SYSTEM "alumnes.dtd" [ ]>

Com d’aquesta:

  1. <!DOCTYPE alumnes SYSTEM "alumnes.dtd">

Definició d'esquemes amb DTD

La part més important d’un sistema de definició de vocabularis és definir com es fa per determinar l’ordre en què els elements poden aparèixer i quin contingut poden tenir, quins atributs poden tenir les etiquetes, etc.

Això, en DTD, es fa definint una sèrie de regles per mitjà d’un sistema d’etiquetes predefinit. A diferència del que passa en XML, en fer una definició en DTD les etiquetes estan definides. Per definir elements i atributs s’hauran de fer servir etiquetes concretes.

Elements

De la mateixa manera que els elements són la base de l’XML, la definició d’aquests elements és la base de la definició de fitxers DTD. Per tant, sempre s’hauran de definir tots els elements que componen el vocabulari i a més especificar-ne el contingut (figura).

Figura Definició d’una DTD

Per tant, és molt senzill definir elements amb DTD: simplement es defineix el nom de l’element i quin és el contingut.

  1. <!ELEMENT nom (#PCDATA)>

Com en XML, els continguts poden ser dades o bé altres elements. Podem definir tres grans grups de maneres de definir els continguts en una DTD:

  • Continguts genèrics
  • Contingut d’elements
  • Contingut barrejat
Continguts genèrics

Hi ha tres continguts genèrics que es poden fer servir per definir elements: ANY, EMPTY i #PCDATA (vegeu la taula).

Taula: Continguts genèrics en una DTD
Valor Significat
ANY El contingut de l’element pot ser qualsevol cosa.
EMPTY L’element no té contingut.
#PCDATA El contingut de l’etiqueta poden ser dades.

Els elements que estiguin definits amb ANY poden contenir qualsevol cosa dins seu. Tant etiquetes, com dades, o fins i tot una barreja de les dues coses.

Si es defineix persona d’aquesta manera:

  1. <!ELEMENT persona ANY>

validarà tant elements que continguin dades:

  1. <persona>Frederic Pi</persona>

com elements que continguin altres elements:

  1. <persona>
  2. <nom>Frederic</nom>
  3. <cognom>Pi</cognom>
  4. </persona>

Per tant, el contingut definit amb ANY és molt potent però també fa que es perdi el control de les coses que es poden validar amb la DTD, ja que ho validarà pràcticament tot.

Els elements en XML a vegades no cal que tinguin cap valor per aportar alguna informació, ja que el nom de l’etiqueta pot tenir algun tipus de significat semàntic.

L’etiqueta <professor> de la línia 4 de l’exemple següent no necessita tenir cap contingut perquè està implícit que la persona a la qual s’assigni l’etiqueta serà un professor.

  1. </persones>
  2. <persona>
  3. <nom>Pere Martí</nom>
  4. <professor/>
  5. </persona>
  6. <persona>
  7. <nom>Marcel Peris</nom>
  8. </persona>
  9. </persones>

Els continguts definits amb EMPTY estan pensats per a les etiquetes que no tindran cap contingut dins seu. Per tant, l’element <professor> de l’exemple anterior es definiria d’aquesta manera:

  1. <!ELEMENT professor EMPTY>

Aquesta definició serveix tant per als elements que es defineixen fent servir el sistema d’una sola etiqueta…

  1. <professor/>

com per als que defineixen les etiquetes d’obertura i tancament.

  1. <professor></professor>

El contingut genèric #PCDATA (parser character data) segurament és el més usat per marcar que una etiqueta només té dades en el seu contingut.

Si partim de l’exemple següent:

  1. <persona>
  2. <nom>Marcel</nom>
  3. <cognom>Puigdevall</nom>
  4. </persona>

Es pot fer servir #PCDATA per definir el contingut dels elements <nom> i <cognom> perquè dins seu només tenen dades.

  1. <!ELEMENT nom (#PCDATA)>
  2. <!ELEMENT cognom (#PCDATA)>

Però no es pot fer servir, en canvi, per definir l’element <persona>, perquè dins seu no hi té només dades sinó que hi té els elements <nom> i <cognom>.

Contingut d'elements

Una de les situacions habituals en un document XML és que un element dins del seu contingut en tingui d’altres.

Tenim diverses possibilitats per definir el contingut d’elements dins d’una DTD:

  • Seqüències d’elements
  • Alternatives d’elements
  • Modificadors

Si mirem l’exemple següent veurem que tenim un element <persona> que conté dos elements, <nom> i <cognom>.

  1. <persona>
  2. <nom>Pere</nom>
  3. <cognom>Martinez</cognom>
  4. </persona>

Per tant, l’element <persona> és un element que té com a contingut una seqüència d’altres elements. En una DTD es defineix aquesta situació definint explícitament quins són els fills de l’element, separant-los per comes.

  1. <!ELEMENT persona (nom,cognom)>

Mai no s’ha d’oblidar que les seqüències tenen un ordre explícit i, per tant, només validaran si l’ordre en què es defineixen els elements és idèntic a l’ordre en què apareixeran en el document XML.

Per tant, si agafem com a referència el document següent:

  1. <menjar>
  2. <dinar>Arròs</dinar>
  3. <sopar>Sopa</sopar>
  4. </menjar>

Si l’element <menjar> es defineix amb la seqüència (<dinar>,<sopar>), aquest validarà, ja que els elements que conté estan en el mateix ordre que en el document.

  1. <!ELEMENT menjar (dinar,sopar)>

Però no validaria, en canvi, si definíssim la seqüència al revés (<sopar>,<dinar>) perquè el processador esperarà que arribi primer un <sopar> i es trobarà un <dinar>.

  1. <!ELEMENT menjar (sopar,dinar)

De vegades, en crear documents XML, succeeix que en funció del contingut que hi hagi en el document pot ser que hagin d’aparèixer unes etiquetes o unes altres.

Si dissenyéssim un XML per definir les fitxes de personal d’una empresa podríem tenir una etiqueta <personal> i després definir el càrrec amb una altra etiqueta. El president es podria definir així:

  1. <personal>
  2. <president>Josep Maria Flaviol</president>
  3. </personal>

I un dels treballadors així:

  1. <personal>
  2. <treballador>Pere Vila</treballador>
  3. </personal>

Podem fer que una DTD validi tots dos casos fent servir l’operador d’alternativa (|).

L’operador alternativa (|) permet que el processador pugui triar entre una de les opcions que se li ofereixen per validar un document XML.

Per tant, podem validar els dos documents XML anteriors definint <personal> d’aquesta manera:

  1. <!ELEMENT personal (treballador|president)>

No hi ha cap limitació definida per posar alternatives. Per tant, en podem posar tantes com ens facin falta.

  1. <!ELEMENT personal (treballador|president|informàtic|gerent)>

També es permet mesclar la definició amb EMPTY per indicar que el valor pot aparèixer o no:

  1. <!ELEMENT alumne (delegat|EMPTY)>

Combinar alternatives amb PCDATA és més complex. Vegeu l’apartat “Problemes en barrejar etiquetes i #PCDATA”.

No hi ha res que impedeixi barrejar les seqüències i les alternatives d’elements a l’hora de definir un element amb DTD. Per tant, es poden fer les combinacions que facin falta entre seqüències i alternatives.

Si tenim un XML que defineix l’etiqueta <cercle> a partir de les seves coordenades del centre i del radi o el diàmetre podem definir l’element combinant les seqüències amb una alternativa. L’etiqueta <cercle> contindrà sempre dins seu les etiquetes <x>,<y> i després pot contenir també <radi> o <diàmetre>:

  1. <!ELEMENT cercle (x,y,(radi|diametre))>

Combinar seqüències i alternatives permet saltar-se les restriccions d’ordre de les seqüències. L’exemple següent ens permet definir una persona a partir de nom i cognom en qualsevol ordre:

  1. <!ELEMENT persona ((cognom,nom)|(nom,cognom))>

En els casos en què les etiquetes es repeteixin un nombre indeterminat de vegades serà impossible especificar-les totes en la definició de l’element. Els modificadors serveixen per especificar quantes instàncies dels elements fills hi pot haver en un element (taula).

Taula: Modificadors acceptats en el contingut dels elements
Modificador Significat
? Indica que l’element tant pot ser que hi sigui com no.
+ Es fa servir per indicar que l’element ha de sortir una vegada o més.
* Indica que pot ser que l’element estigui repetit un nombre indeterminat de vegades, o bé no ser-hi.

Per tant, si volem especificar que una persona pot ser identificada per mitjà del nom i un o més cognoms podríem definir l’element <persona> d’aquesta manera:

  1. <!ELEMENT persona (nom,cognom+)>

Com que aquesta expressió indica que cognom ha de sortir una vegada, podrà validar tant aquest exemple:

  1. <persona>
  2. <nom>Joan</nom>
  3. <cognom>Puig</cognom>
  4. </persona>

com aquest altre:

  1. <persona>
  2. <nom>Joan</nom>
  3. <cognom>Puig</cognom>
  4. <cognom>Garcia</cognom>
  5. </persona>

És evident que aquesta no és la millor manera de definir el que volíem, ja que definit d’aquesta manera també ens acceptarà persones amb 3 cognoms, 4 cognoms, o més.

Per tant, el modificador ideal per forçar que només hi pugui haver un o dos cognoms seria ?, emprant-lo de la manera següent:

  1. <!ELEMENT persona (nom, cognom, cognom?)>

El modificador * aplicat al mateix exemple ens permetria acceptar que alguna persona no tingués cognoms o que en tingui un nombre indeterminat:

  1. <!ELEMENT persona (nom, cognom*)>

Els modificadors aplicats darrere d’un parèntesi impliquen aplicar-los a tots els elements de dintre dels parèntesis:

  1. <!ELEMENT escriptor ((llibre,data)*|articles+) >
Contingut barrejat

El contingut barrejat es va pensar per poder definir continguts que continguin text que es va intercalant amb d’altres elements, com per exemple:

  1. <carta>Benvolgut <empresa>Ferros Puig</empresa>:
  2.  
  3. Sr. <director>Manel Garcia</director>, li envio aquesta carta per comunicar-li que li hem enviat la seva comanda <comanda>145</comanda> a l'adreça que ens va proporcionar.
  4.  
  5. Atentament, <empresa>Ferreteria, SA</empresa>
  6.  
  7. </carta>

El contingut barrejat es defineix definint el tipus #PCDATA (sempre en primer lloc) i després s’afegeixen els elements amb l’ajuda de l’operador d’alternativa, |, i s’acaba tot el grup amb el modificador *.

  1. <!ELEMENT carta (#PCDATA|empresa|director|comanda)*>

S’ha de tenir en compte que aquest contingut només serveix per controlar que els elements hi puguin ser però que no hi ha cap manera de controlar en quin ordre apareixeran els diferents elements.

Per tant, podem definir una DTD que validi l’exemple presentat al principi d’aquest apartat de la manera següent:

  1. <!ELEMENT carta (#PCDATA|empresa|director )*>
  2. <!ELEMENT empresa (#PCDATA)>
  3. <!ELEMENT director(#PCDATA)>

Atributs en DTD

En les DTD s’han d’especificar quins són els atributs que es faran servir en cada una de les etiquetes explícitament. La declaració d’atributs es fa amb l’etiqueta especial ATTLIST, que es defineix tal com es veu en la figura.

Figura Definició d’un atribut en DTD

Els dos primers valors de la definició de l’atribut són el nom de l’etiqueta i el nom de l’atribut, ja que en definir un atribut sempre s’especifica a quina etiqueta pertany. Una de les crítiques que s’han fet a les DTD ha estat que no s’hi poden fer atributs genèrics. Si algú vol definir un atribut que sigui compartit per tots els elements del seu vocabulari ha d’anar especificant l’atribut per a tots i cada un dels elements.

Per definir un atribut anomenat nom que pertanyi a l’element <persona> ho podem fer d’aquesta manera:

  1. <!ATTLIST persona nom CDATA #IMPLIED>
Especificar múltiples atributs

Si un element necessita diversos atributs haurem d’especificar tots els atributs en diverses línies ATTLIST. Per exemple, definim els atributs nom i cognoms de l’element <persona> d’aquesta manera:

  1. <!ATTLIST persona nom CDATA #REQUIRED>
  2. <!ATTLIST persona cognom CDATA #REQUIRED>

O bé es pot fer la definició dels dos atributs amb una sola referència ATTLIST:

  1. <!ATTLIST persona nom CDATA #REQUIRED
  2. cognom CDATA #REQUIRED>

Les dues declaracions anteriors ens permetrien validar els atributs de l’element <persona> d’aquest exemple:

  1. <persona nom="Frederic" cognom="Pi" />
Atributs d'ATTLIST

Els elements ATTLIST a part de tipus de dades també poden tenir atributs que permeten definir característiques sobre els atributs. Aquests atributs es poden veure en la taula.

Taula: Atributs d’ATTLIST
Atribut Significat
#IMPLIED L’atribut és opcional. Els elements el poden tenir o no.
#REQUIRED L’atribut és obligatori. L’element l’ha de tenir definit o no validarà.
#FIXED Es fa servir per definir atributs que tenen valors constants i immutables. S’ha d’especificar, ja que és permanent.
#DEFAULT Permet especificar valors per defecte en els atributs.

Per tant, si es defineix un atribut d’aquesta manera:

  1. <!ATTLIST equip posicio ID #REQUIRED>

s’està obligant que quan es defineixi l’element <equip> en un document XML s’especifiqui obligatòriament l’atribut posicio i que a més el seu valor no es repeteixi en el document, ja que és de tipus ID.

En canvi, definint l’atribut DNI de <persona> amb #IMPLIED es permetrà que en crear el document XML l’atribut DNI hi pugui ser o no.

  1. <!ATTLIST persona dni NMTOKEN #IMPLIED>

En definir un atribut com a #FIXED o com a #DEFAULT se li ha d’especificar el valor. Aquest valor s’especifica a continuació de l’atribut i ha d’anar entre cometes:

  1. <!ATTLIST document versio CDATA #FIXED "1.0">
  2. <!ATTLIST document codificacio NMTOKEN #DEFAULT "UTF-8">

Els atributs de tipus #FIXED han de tenir el valor especificat en la definició i aquest no es pot canviar, mentre que els valors definits amb #DEFAULT sí que poden ser canviats.

Tipus de dades

El tercer paràmetre d’una declaració ATTLIST serveix per definir quins tipus de dades pot tenir l’atribut. Els atributs en DTD no fan servir els tipus de dades dels elements sinó que en defineixen de propis. Els tipus de dades dels atributs es poden veure a la taula.

Taula: Tipus de dades dels atributs d’una DTD
Tipus Significat
CDATA Pot contenir qualsevol cadena de caràcters acceptable. Es pot fer servir per a preus, URL, adreces electròniques, etc.
Enumeracions Es fan servir per definir que l’atribut ha de tenir un dels valors especificats.
ID L’atribut es podrà fer servir com a identificador d’un element. El seu valor serà únic.
IDREF o IDREFS El valor són referències a un ID que ha d’existir. El plural és per definir que és una llista.
ENTITY o ENTITIES Les entitats permeten definir constants per al document i, per tant, el valor de l’atribut ha de ser una entitat.
NMTOKEN o NMTOKENS Especifiquen cadenes de caràcters que només tinguin caràcters permesos per XML. Per tant, no es permeten els espais.
NOTATION Permet que l’atribut sigui d’una notació declarada anteriorment.
CDATA

El tipus CDATA és un tipus de dades pràcticament idèntic al tipus #PCDATA de les etiquetes. En un CDATA es pot posar qualsevol dada en format de text tant si té espais com si no en té.

Per tant, la declaració següent:

  1. <!ATTLIST empresa nom CDATA #REQUIRED>

permetria definir etiquetes empresa com aquestes:

  1. <empresa nom="Microsoft Corporation"/>
  2. <empresa nom="6tem"/>

És important recordar que els nombres també validarien amb un tipus CDATA perquè interpretats en forma de text no deixen de ser simplement caràcters:

  1. <empresa nom="12"/>
Enumeracions

Els atributs també poden ser especificats definint-hi quins poden ser els valors correctes per a un atribut. Aquests valors s’especifiquen literalment amb l’operador de selecció:

  1. <!ATTLIST mòdul inici (setembre|febrer) #IMPLIED>

Per tant, segons la definició, l’atribut inici ha d’existir i només pot tenir els valors “setembre” o “febrer”.

Es poden posar múltiples opcions en una enumeració. Per exemple, podem comprovar que el valor de l’atribut sigui un nombre entre 1 i 12 d’aquesta manera:

  1. <!ATTLIST mòdul nombre(1|2|3|4|5|6|7|8|9|10|11|12)>
ID

El tipus de dades ID serveix per definir atributs que es puguin usar com a identificadors d’un element dins del document. Cal tenir en compte que:

  • Els valors assignats no es poden repetir dins del document. Això els fa ideals per fer servir el tipus ID per identificar únicament els elements d’un document.
  • Els valors han de començar per una lletra o un subratllat.

Els valors de l’atribut posicio de la definició següent no es podran repetir dins del mateix document:

  1. <!ATTLIST equip posicio ID #REQUIRED>

Per tant, si es fes servir la declaració anterior per validar aquest document es generaria un error de validació en l’atribut posició, ja que es repeteix el valor “primer” en els dos elements.

  1. <classificació>
  2. <equip posició="primer">F.C.Barcelona</equip>
  3. <equip posició="primer">Reial Madrid</equip>
  4. </classificació>

Perquè validi aquest document cal assegurar-se que els atributs ID no tinguin el mateix valor:

  1. <classificació>
  2. <equip posició="primer">F.C.Barcelona</equip>
  3. <equip posició="segon">Reial Madrid</equip>
  4. </classificació>
IDREF / IDREFS

Els valors IDREF i IDREFS es fan servir per definir valors d’atributs que fan referència a elements que tinguin un valor de tipus ID. Només tenen sentit en vocabularis que tinguin atributs amb valor ID.

IDREF es fa servir per fer referència a un valor ID:

  1. <!ATTLIST recepta id ID #REQUIRED>
  2. <!ATTLIST ingredient ref IDREF #REQUIRED

Amb la declaració que s’acaba d’especificar es pot validar un document com el següent:

  1. <llibre-cuina>
  2. <receptes>
  3. <recepta id="recepta1">Patates fregides</recepta>
  4. <recepta id="recepta2">Patates bullides</recepta>
  5. </receptes>
  6. <ingredients>
  7. <ingredient ref="recepta1">Oli</ingredient>
  8. <ingredient ref="recepta2">Aigua</ingredient>
  9. </ingredients>
  10. </llibre-cuina>

IDREFS permet especificar una llista de valors ID en el mateix atribut. Per exemple, si es defineix l’atribut ingredient amb IDREFS:

  1. <!ATTLIST ingredient ref IDREFS #REQUIRED>

Es podria posar una llista d‘ID com a valor de l’atribut:

  1. <ingredient ref="recepta1 recepta2">Patates</ingredient>
NMTOKEN / NMTOKENS

Els tipus NMTOKEN permeten especificar que els atributs poden tenir qualsevol caràcter acceptat per l’XML.

Vegeu l’apartat “Noms vàlids XML” en aquesta unitat.

Així, la declaració següent:

  1. <!ATTLIST home naixement NMTOKEN #REQUIRED>

permetrà validar aquest element:

  1. <home naixement="1970" />

Però no ho farà amb aquest altre, perquè té un espai:

  1. <home naixement="segle XX" />

El valor en plural NMTOKENS permet especificar una llista de valors en comptes d’un de sol:

  1. <coordenades posicio="x y"/>
NOTATION

Es fa servir per permetre valors que han estat declarats com a notation amb l’etiqueta <!NOTATION. Es fa servir per especificar dades no-XML.

Sovint es fa servir per declarar tipus MIME:

  1. <!NOTATION GIF SYSTEM "image/gif">
  2. <!NOTATION JPG SYSTEM "image/jpeg">
  3. <!NOTATION PNG SYSTEM "image/png">
  4. <!ATTLIST persona
  5. photo_type NOTATION (GIF | JPG | PNG) #IMPLIED>
ENTITY / ENTITIES

Indica que el valor és una referència a un valor extern que no s’ha de processar. En general solen contenir valors binaris externs.

Fer servir ENTITY implicarà haver declarat l’entitat amb <!ENTITY:

<!ATTLIST persona foto ENTITY #IMPLIED
<!ENTITY pere SYSTEM "Pere.jpg">

Permetrà que es defineixi l’atribut persona amb el valor de l’entitat i que automàticament sigui associat a la imatge:

  1. <persona nom="pere" />

Altres

Les etiquetes <!ELEMENT> i <!ATTLIST> no són les úniques que es poden fer servir per declarar DTD. N’hi ha algunes més que en determinats casos es poden fer servir per crear una DTD.

En la taula se’n poden veure algunes, i també un breu resum de què és el que fan.

Taula: Altres etiquetes possibles en una DTD
Etiqueta Es fa servir per …
<!ENTITY> Definir referències a fitxers externs que no s’han de processar.
<!NOTATION> Incloure dades que no siguin XML en el document.
<!INCLUDE> Fer que parts de la DTD s’afegeixin al processament.
<!IGNORE> Fer que parts de la DTD siguin ignorades pel processador.

Limitacions

Una de les crítiques que s’ha fet a l’ús de DTD per definir llenguatges XML és que no està basat en XML. La DTD no segueix la manera de definir els documents d’XML i, per tant, per fer-lo servir, cal aprendre un nou llenguatge. Si la DTD estigués basada en XML no caldria conèixer de quina manera s’han de definir els elements, marcar les repeticions, els elements buits, etc., ja que es faria amb elements.

Tot i així, el fet que DTD no sigui un llenguatge XML és un problema menor si es tenen en compte les altres limitacions que té:

  • No comprova els tipus
  • Presenta problemes en barrejar etiquetes i #PCDATA
  • Només accepta expressions deterministes

La DTD no comprova els tipus

Un dels problemes més importants que ens trobarem a l’hora d’usar DTD per definir el nostre vocabulari és que no té cap manera de comprovar els tipus de dades que contenen els elements. Sovint els noms dels elements ja determinen que el contingut que hi haurà serà d’un tipus determinat (un nombre, una cadena de caràcters, una data, etc.) però la DTD no deixa que se li especifiqui quin tipus de dades s’hi vol posar.

Per tant, si algú emplena amb qualsevol cosa un element que es digui <dia>, el document serà vàlid a pesar que el contingut no sigui una data:

  1. <dia>xocolata</dia>

En no poder comprovar el tipus del contingut, una limitació important afegida és que no hi ha cap manera de poder posar-hi restriccions. Per exemple, no podem definir que volem que una data estigui entre els anys 1900 i 2012.

Problemes en barrejar etiquetes i #PCDATA

Una limitació més complexa de veure és que no es poden barrejar etiquetes i #PCDATA en expressions si el resultat no és el que es coneix com a “contingut barrejat”.

Vegeu l’apartat “Contingut barrejat” d’aquesta unitat.

Un exemple d’això consistiria a fer una definició d’un exercici com un enunciat de text, però que hi pugui haver diversos apartats que també continguin text.

  1. <exercici>
  2. Llegiu el text "Validació de documents XML" i responeu les preguntes següents:
  3. <apartat numero="1">Què volen dir les sigles XML?</apartat>
  4. <apartat numero="2">Què és un DTD?</apartat>
  5. </exercici>

El més senzill seria mesclar un #PCDATA i l’etiqueta <apartat>, però és incorrecte:

  1. <!ELEMENT exercici (#PCDATA | apartat*)>

A més, no es permet que es facin declaracions duplicades d’elements, o sigui que tampoc no ho podem arreglar amb:

  1. <!ELEMENT exercici(#PCDATA)>
  2. <!ELEMENT exercici(apartat*)>

L’única manera de combinar-ho seria fer servir la fórmula del contingut barrejat:

  1. <!ELEMENT exercici(#PCDATA|apartat)*>

Només accepta expressions deterministes

Una altra de les limitacions de les DTD és que obliga que les expressions hagin de ser sempre deterministes.

Si ens mirem un document DTD:

  1. <!ELEMENT classe(professor|alumnes)>
  2. <!ELEMENT professor (nom,cognoms)>
  3. <!ELEMENT alumnes (alumne*) >
  4. <!ELEMENT alumne (nom,cognom) >
  5. <!ELEMENT nom (#PCDATA)>
  6. <!ELEMENT cognom (#PCDATA)>

Quan s’analitza un fitxer XML es defineix quina és l’arrel de la DTD. Si considerem que l’arrel és l’element <classe>, el procés de validació començarà en la primera línia que ens diu que perquè el document sigui vàlid després de l’arrel hi ha d’haver un element <professor> o bé un element <alumnes>. Si el que arriba és un <professor> el procediment de validació passarà a avaluar la segona i si arriba un <alumne> passarà a la tercera. Si seguim amb l’avaluació veurem que realment el validador sempre que es troba amb una alternativa acabarà anant a una sola expressió. Això és perquè la DTD analitzada és determinista.

El mateix ho podem fer amb una expressió més complexa que contingui diferents elements dins de l’alternativa. El validador ha de poder triar, en llegir el primer element, amb quina de les alternatives s’ha de quedar. Si m’arriba un element <nom> em quedo amb l’alternativa de l’esquerra, nom,cognoms, i si m’arriba un <àlies> em quedo amb la de la dreta, àlies,nom,cognoms.

  1. <!ELEMENT persona(nom,cognom|àlies,nom,cognom)>

Si en algun moment algú crea un document que no sigui determinista la validació fallarà. Això passarà si en algun moment el validador es troba que no pot decidir quina és l’alternativa correcta.

  1. <!ELEMENT terrestre(persona, home | persona, dona)>

Quan des de l’element <terrestre> ens arribi un element <persona> el processador no tindrà cap manera de determinar si estem fent referència a l’element <persona> de l’expressió de la dreta persona,home o bé el de l’esquerra persona,dona, i per tant fallarà perquè té dues opcions possibles. És una expressió no determinista.

Sovint les expressions no deterministes les podem expressar d’una altra manera, de manera que esdevinguin deterministes. L’exemple anterior es podia haver escrit d’una manera determinista perquè en arribar un element <persona> no hi hagi dubtes.

  1. <!ELEMENT terrestre(persona,(home|dona))>

Es força que sempre aparegui un element <persona> i després hi haurà l’alternativa entre un <home> o un <dona> que és determinista.

Les expressions no deterministes són més corrents del que sembla, ja que els modificadors les poden provocar i pot ser que no ho semblin. Si es volgués escriure una expressió que determini un llibre a un llibre a partir de l’autor o el títol en qualsevol ordre:

  1. <!ELEMENT llibre(autor?,titol?|titol?,autor?)>

Però l’expressió anterior és incorrecta, ja que si el validador es troba un <autor> no sap si és l’autor del costat dret de la condició autor?,titol? o bé és el del costat esquerre que no té títol, titol?,autor?.

L’expressió determinista idèntica a l’anterior seria:

  1. <!ELEMENT llibre(autor,titol?|titol,autor?|EMPTY)>

Exemple de creació d'una DTD

Les DTD es continuen fent servir perquè són senzilles de crear però cal recordar sempre les limitacions que tenen, i tenir present que no sempre s’adaptaran perfectament a allò que es vol fer.

Enunciat

Una empresa ha preparat una botiga d’Internet que genera les comandes dels clients en un format XML que s’envia al programa de gestió de manera automàtica.

Els XML que es generen contenen dades del client i de la comanda que s’ha fet:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE comanda SYSTEM "comanda.dtd">
  3. <comanda numero="26" dia="2011-12-01" >
  4. <client codi="20">
  5. <nom>Frederic</nom>
  6. <cognom>Garcia</cognom>
  7. </client>
  8. <articles>
  9. <article>
  10. <descripció>Yoda Mimobot USB Flash Drive 8GB</descripció>
  11. <quantitat>5</quantitat>
  12. <preu>38.99</preu>
  13. </article>
  14. <article>
  15. <descripció>Darth Vader Half Helmet Case for iPhone</descripció>
  16. <quantitat>2</quantitat>
  17. <preu>29.95</preu>
  18. </article>
  19. </articles>
  20. <total valor="254,85"/>
  21. </comanda>

Abans de passar-lo al programa han decidit que per tenir més seguretat es farà un pas previ que consistirà a validar que el document. Per això necessiten que es generi la DTD.

Resolució

S’hauran de fer dues coses:

  • Associar les regles al fitxer XML.
  • Crear un fitxer amb les regles, que s’anomenarà “comanda.dtd”.
Associar el fitxer XML

En la segona línia del document s’hi especifica la regla DOCTYPE per associar el fitxer amb la DTD.

  1. <?xml version="1.0"?>
  2. <!DOCTYPE albarà SYSTEM "comanda.dtd">
Crear les regles

No hi ha una manera única de crear una DTD però normalment seguir un sistema pot ajudar a evitar errors. El sistema que es farà servir per resoldre el sistema consisteix a començar per l’arrel i anar definint els fulls per nivells.

L’arrel del document és l’element <comanda>, que té tres fills:

  1. <!ELEMENT comanda (client, articles, total)>

També té dos atributs, numero i dia, que només poden tenir valors sense espais i, per tant, seran NMTOKEN. Són dades obligatòries per motius fiscals.

  1. <!ATTLIST comanda numero NMTOKEN #REQUIRED>
  2. <!ATTLIST comanda dia NMTOKEN #REQUIRED>

Un cop definit el primer nivell podem passar a definir els altres. D’una banda l’element <client>, que té un atribut per als clients existents i no en tindrà per als nous:

  1. <!ELEMENT client (nom, cognom)>
  2. <!ATTLIST client codi NMTOKEN #IMPLIED >

D’altra banda l’element <total>, que és buit però té un atribut:

  1. <!ELEMENT total EMPTY>
  2. <!ATTLIST total valor CDATA #REQUIRED >

També l’element <articles>, que contindrà una llista dels articles que ha comprat el client. Com que no tindria sentit fer una comanda sense articles el modificador que es farà servir serà +.

  1. <!ELEMENT articles (article+)>

Per la seva part, desenvolupar <article> tampoc no portarà gaires problemes:

  1. <!ELEMENT article (descripció, quantitat, preu)>

Per acabar només queden els elements que contenen dades:

  1. <!ELEMENT nom (#PCDATA)>
  2. <!ELEMENT cognom (#PCDATA)>
  3. <!ELEMENT descripció (#PCDATA)>
  4. <!ELEMENT quantitat (#PCDATA)>
  5. <!ELEMENT preu (#PCDATA)>

El fitxer resultant serà:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!ELEMENT comanda (client, articles, total) >
  3. <!ATTLIST comanda numero NMTOKEN #REQUIRED >
  4. <!ATTLIST comanda dia NMTOKEN #REQUIRED>
  5.  
  6. <!ELEMENT client (nom,cognom) >
  7. <!ATTLIST client codi NMTOKEN #IMPLIED >
  8.  
  9. <!ELEMENT total EMPTY>
  10. <!ATTLIST total valor CDATA #REQUIRED >
  11.  
  12. <!ELEMENT articles (article+)>
  13. <!ELEMENT article (descripció, quantitat, preu)>
  14.  
  15. <!ELEMENT nom (#PCDATA)>
  16. <!ELEMENT cognom (#PCDATA)>
  17. <!ELEMENT descripció (#PCDATA)>
  18. <!ELEMENT quantitat (#PCDATA)>
  19. <!ELEMENT preu (#PCDATA)>

W3C XML Schema Definition Language

En l’especificació XML es fa referència a les DTD com a mètode per definir vocabularis XML, però les DTD tenen una sèrie de limitacions que van portar el W3C a definir una nova especificació. Aquesta especificació va rebre el nom de W3C XML Schema Definition Language (que popularment s’anomena XML Schema o XSD), i es va crear per substituir la DTD com a mètode de definició de vocabularis per a documents XML.

Es pot trobar l’especificació més recent a www.w3.org/XML/Schema.

Relax NG

Durant l’especificació molts membres del grup de treball van considerar que el que sortia era massa complex i van desenvolupar alternatives més simples, entre les quals destaca Relax NG (www.relaxng.org).

Com que Relax NG es va fer més tard que DTD i XSD, va poder solucionar alguns dels problemes.

Els esquemes es poden definir en dues versions de Relax NG, una basada en XML i una altra que no ho està, anomenada Relax NG Compact, i que està pensada per generar documents d’esquemes més petits i compactes.

L’èxit d’XSD ha estat molt gran i actualment es fa servir per a altres tasques a part de simplement validar XML. També es fa servir en altres tecnologies XML com XQuery, serveis web, etc.

Les característiques més importants que aporta XSD són:

  • Està escrit en XML i, per tant, no cal aprendre un llenguatge nou per definir esquemes XML.
  • Té el seu propi sistema de dades, de manera que es podrà comprovar el contingut dels elements.
  • Suporta espais de noms per permetre barrejar diferents vocabularis.
  • Permet ser reutilitzat i segueix els models de programació com herència d’objectes i substitució de tipus.

Associar un esquema a un fitxer XML

A diferència del que passa en altres llenguatges de definició –com per exemple en les DTD, en què l’associació s’ha d’especificar en el document XML–, per validar un XML amb un XSD no cal modificar el fitxer XML. De totes maneres, també és possible fer-ho definint-hi l’espai de noms.

Per associar un document XML a un document d’esquema cal definir-hi l’espai de noms amb l’atribut xmlns, i per mitjà d’un dels atributs del llenguatge definir-hi quin és el fitxer d’esquema:

  1. <lliga xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:noNamespaceSchemaLocation="lliga.xsd">

Es poden definir les referències al fitxer d’esquema de dues maneres, que podem veure en la taula.

Taula: Definir referències a un esquema
Atribut Significat
noNamespaceSchemaLocation S’empra quan no es faran servir espais de noms en el document.
schemaLocation S’empra quan es fan servir explícitament els noms dels espais de noms en les etiquetes.

Definir un fitxer d'esquema

L’XSD està basat en XML i, per tant, ha de complir amb les regles d’XML:

  • Tot i que no és obligatori normalment sempre es comença el fitxer amb la declaració XML.
  • Només hi ha un element arrel, que en aquest cas és <schema>.

Com que no s’està generant un document XML lliure sinó que s’està fent servir un vocabulari concret i conegut per poder fer servir els elements XML sempre s’hi haurà d’especificar l’espai de noms d’XSD: ”http://www.w3.org/2001/XMLSchema”.

  1. <?xml version="1.0" ?>
  2. <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  3. ...
  4. </xs:schema>

En la declaració d’aquest espai de noms s’està definint que xs és l’àlies per fer referència a totes les etiquetes d’aquest espai de noms. Els àlies per a XSD normalment són xs o xsd, però en realitat no importa quin es faci servir sempre que es faci servir el mateix en tot el document.

Etiquetes d'XSD

L’XSD defineix moltes etiquetes i no es veuran totes aquí. Podeu trobar totes les etiquetes possibles en l’especificació www.w3.org/TR/xmlschema11-1 .

L’etiqueta <schema> pot tenir diferents atributs, alguns dels quals podem veure en la taula.

Taula: Atributs de l’etiqueta <schema>
Atribut Significat
attributeFormDefault Pot ser qualified si fem que sigui obligatori posar l’espai de noms davant dels atributs o unqualified si no ho fem. Per defecte es fa servir unqualified.
elementFormDefault Serveix per definir si cal l’espai de noms davant del nom. Pot prendre els valors qualified i unqualified.
version Permet definir quina versió del document d’esquemes estem definint (no és la versió d’XML Schemas).

A partir de l’etiqueta arrel ja es poden començar a definir les etiquetes del vocabulari que es vol crear.

Definició d'elements

Els elements es defineixen tal com es veu a la figura, fent servir l’etiqueta <element>, i amb atributs s’hi especifica com a mínim el nom i opcionalment el tipus de dades que contindrà l’element.

Figura Definició d’un element

L’XSD divideix els elements en dos grans grups basant-se en les dades que contenen:

  • Elements amb contingut de tipus simple: són elements sense atributs que només contenen dades.
  • Elements amb contingut de tipus complex: són elements que poden tenir atributs, no tenir contingut o contenir elements.

A partir de la definició es pot veure que gairebé sempre hi haurà algun tipus complex, ja que l’arrel normalment contindrà altres elements.

Elements amb contingut de tipus simple

Es consideren elements amb contingut de tipus simple aquells que no contenen altres elements ni tenen atributs.

La versió 1.1 d’XSD defineix una cinquantena de tipus de dades diferents, que es poden trobar a la seva definició (www.w3.org/TR/xmlschema11-2). Entre els més usats en destaquen els de la taula.

Taula: Tipus de dades més usats en XSD
Tipus Dades que s’hi poden emmagatzemar
string Cadenes de caràcters
decimal Valors numèrics
boolean Només pot contenir ‘true’ o ‘false’ o (1 o 0)
date Dates en forma (AAAA-MM-DD)
anyURI Referències a llocs (URL, camins de disc…)
base64binary Dades binàries codificades en base64
integer Nombres enters

A partir dels tipus bàsics, l’estàndard en crea d’altres amb l’objectiu de tenir tipus de dades que es puguin adaptar millor als objectius de qui dissenya l’esquema. En aquest sentit apareixen els tipus anomenats positiveInteger, nonNegativeInteger, gYearMonth, unsignedInt

Els tipus de dades permeten restringir els valors que contindran les etiquetes XML. Per exemple, si es parteix de la definició següent:

  1. <xs:element name="posicio" type="xs:integer"/>
  2. </xs:schema>

Només s’aconseguirà validar un element si el seu contingut és un nombre enter. Per exemple, l’exemple següent no validarà:

  1. <posicio>Primer</posicio>

En la taula es poden veure exemples de definicions d’elements i valors que hi validen.

Taula: Exemples d’elements i què s’hi pot validar
Etiqueta Exemple
<xs:element name=“dia” type=“xs:date” /> <dia>2011-09-15</dia>
<xs:element name=“alçada” type=“xs:integer”/> <alçada>220</alçada>
<xs:element name=“nom” type=“xs:string”/> <nom>Pere Puig</nom>
<xs:element name=“mida” type=“xs:float”/> <mida>1.7E2</mida>
<xs:element name=“lloc” type=“xs:anyURI”/> <lloc>http://www.ioc.cat</lloc>

Quan es defineix una etiqueta en XSD s’està definint que l’etiqueta haurà de sortir en el document XML una vegada. És bastant habitual que hi hagi etiquetes que es repeteixin un nombre determinat de vegades. En XSD això s’ha simplificat per mitjà d’uns atributs de l’etiqueta <element> que determinen la cardinalitat dels elements:

  • minOccurs: permet definir quantes vegades ha de sortir un element com a mínim. Un valor de ‘0’ indica que l’element pot no sortir.
  • maxOccurs: serveix per definir quantes vegades com a màxim pot sortir un element. unbounded implica que no hi ha límit en les vegades que pot sortir.

Fent servir els atributs es pot definir que l’element <nom> ha de sortir una vegada i l’element <cognom> un màxim de dues vegades.

  1. <xs:element name="nom" />
  2. <xs:element name="cognom" maxOccurs="2"/>

També es poden donar valors als elements amb els atributs fixed, default i nullable.

L’atribut fixed permet definir un valor obligatori per a un element:

  1. <xs:element name="centre" type="xs:string" fixed="IOC"/>

De manera que només es podrà definir el contingut amb el valor especificat (o sense res):

  1. <centre />
  2. <centre>IOC</centre>

Però mai un valor diferent de l’especificat:

  1. <centre>Institut Cendrassos</centre>

A diferència de fixed, default assigna un valor per defecte però deixa que sigui canviat en el contingut de l’etiqueta.

  1. <xsi:element name="centre" type="xs:string" default="IOC" />

La definició permetria validar amb els tres casos següents:

  1. <centre />
  2. <centre>IOC</centre>
  3. <centre>Institut Cendrassos</centre>

L’atribut nullable es fa servir per dir si es permeten continguts nuls. Per tant, només pot prendre els valors yes o no.

Tipus simples personals

Com que a vegades pot interessar definir valors per als elements que no han de coincidir necessàriament amb els estàndards, l’XSD permet definir tipus de dades personals. Per exemple, si es vol un valor numèric però que no accepti tots els valors sinó un subconjunt dels enters.

Per definir tipus simples personals no es posa el tipus a l’element i s’hi defineix un fill <simpleType>.

  1. <xs:element name="persona">
  2. <xs:simpleType>
  3. ...
  4. </xs:simpleType>
  5. </xs:element>

Dins de simpleType s’especifica quina és la modificació que s’hi vol fer. El més corrent és que les modificacions sigui fetes amb list, union, restriction o extension.

A pesar que es poden definir llistes de valors no es recomana gaire fer-ne servir. La majoria dels experts creuen que és millor definir els valors de la llista fent servir repeticions d’etiquetes.

Fer servir list permetrà definir que un element pot contenir llistes de valors. Per tant, per especificar que un element <partits> pot contenir una llista de dates es definiria:

  1. <xs:element name="partits">
  2. <xs:simpleType>
  3. <xs:list itemType="xs:date"/>
  4. </xs:simpleType>
  5. </xs:element>

L’etiqueta validaria amb una cosa com:

  1. <partits> 2011-01-07 2011-01-15 2011-01-21</partits>

Els elements simpleType també es poden definir amb un nom fora dels elements i posteriorment usar-los com a tipus de dades personal.

  1. <xs:simpleType name="dies">
  2. <xs:list itemType="xs:date"/>
  3. </xs:simpleType>
  4.  
  5. <xs:element name="partits" type="dies"/>

Fent servir els tipus personalitzats amb nom es poden crear modificacions de tipus union. Els modificadors union serveixen per fer que es puguin mesclar tipus diferents en el contingut d’un element.

La definició de l’element <preu> farà que l’element pugui ser de tipus valor o de tipus simbol.

  1. <xs:element name="preus">
  2. <xs:sipleType>
  3. <xs:union memberTypes="valor simbol"/>
  4. </xs:simpleType>
  5. </xs:element>

Amb això li podríem assignar valors com aquests:

  1. <preus>25 €</preus>

Però sense cap mena de dubte el modificador més interessant és el que permet definir restriccions als tipus base. Amb el modificador restriction es poden crear tipus de dades en què només s’acceptin alguns valors, que les dades compleixin una determinada condició, etc.

L’element <naixement> només podrà tenir valors enters entre 1850 i 2011 si es defineix d’aquesta manera:

  1. <xs:simpleType name="any_naixement">
  2. <xs:restricion base="xs:integer">
  3. <xs:maxInclusive value="2011"/>
  4. <xs:minInclusive value="1850"/>
  5. </xs:restriction>
  6. </xs:simpleType>
  7.  
  8. <xs:element name="naixement" type="any_naixement"/>

Es poden definir restriccions de molts tipus per mitjà d’atributs (taula). Normalment els valors de les restriccions s’especifiquen en l’atribut value:

Taula: Atributs que permeten definir restriccions en XSD
Elements Resultat
maxInclusive / maxExclusive Es fa servir per definir el valor numèric màxim que pot agafar un element.
minInclusive / minExclusive Permet definir el valor mínim del valor d’un element.
length Amb lenght restringim la llargada que pot tenir un element de text. Podem fer servir <xs:minLength> i <xs:maxLenght> per ser més precisos.
enumeration Només permet que l’element tingui algun dels valors especificats en les diferents línies <enumeration>.
totalDigits Permet definir el nombre de dígits d’un valor numèric.
fractionDigits Serveix per especificar el nombre de decimals que pot tenir un valor numèric.
pattern Permet definir una expressió regular a la qual el valor de l’element s’ha d’adaptar per poder ser vàlid.

Per exemple, el valor de l’element <resposta> només podrà tenir un dels tres valors “A”, “B” o “C” si es defineix d’aquesta manera:

  1. <xs:element name="resposta">
  2. <xs:simpleType>
  3. <xs:enumeration value="A"/>
  4. <xs:enumeration value="B"/>
  5. <xs:enumeration value="C"/>
  6. </xs:simpleType>
  7. </xs:element>

Una de les restriccions més interessants són les definides per l’atribut pattern, que permet definir restriccions a partir d’expressions regulars. Com a norma general tenim que si s’especifica un caràcter en el patró aquest caràcter ha de sortir en el contingut; les altres possibilitats les podem veure a la taula

Taula: Definició d’expressions regulars
Símbol Equivalència
. Qualsevol caràcter
\d Qualsevol dígit
\D Qualsevol caràcter no dígit
\s Caràcters no imprimibles: espais, tabuladors, salts de línia…
\S Qualsevol caràcter imprimible
x* El de davant de * ha de sortir 0 o més vegades
x+ El de davant de + ha de sortir 1 o més vegades
x? El de davant de ? pot sortir o no
[abc] Hi ha d’haver algun caràcter dels de dins
[0-9] Hi ha d’haver un valor entre el dos especificats, amb aquests inclosos
x{5} Hi ha d’haver 5 vegades el que hi hagi al davant dels claudàtors
x{5,} Hi ha d’haver 5 o més vegades el de davant
x{5,8} Hi ha d’haver entre 5 i 8 vegades el de davant

Fent servir aquest sistema es poden definir tipus de dades molt personalitzats. Per exemple, podem definir que una dada ha de tenir la forma d’un DNI (8 xifres, un guió i una lletra majúscula) amb aquesta expressió:

  1. <xs:simpleType name="dni">
  2. <xs:restriction base="xs:string">
  3. <xs:pattern value="[0-9]{8}-[A-Z]"/>
  4. </xs:restriction>
  5. </xs:simpleType>

A part de les restriccions també hi ha l’element extension, que serveix per afegir característiques extra als tipus. No té gaire sentit que surti en elements de tipus simple però es pot fer servir, per exemple, per afegir atributs als tipus simples (cosa que els converteix en tipus complexos).

Elements amb contingut de tipus complex

Els elements amb contingut de tipus complex són aquells que tenen atributs, contenen altres elements o no tenen contingut.

Els elements amb contingut complex han rebut moltes crítiques perquè es consideren massa complicats, però s’han de fer servir perquè en tots els fitxers d’esquema normalment hi haurà un tipus complex: l’arrel del document.

Es considera que hi ha quatre grans grups de continguts de tipus complex:

  1. Els que en el seu contingut només tenen dades. Per tant, són com els de tipus simples però amb atributs.
  2. Els elements que en el contingut només tenen elements.
  3. Els elements buits.
  4. Els elements amb contingut barrejat.

Els elements amb tipus complex es defineixen especificant que el tipus de dades de l’element és <xs:complexType>.

  1. <xs:element name="classe">
  2. <xs:complexType>
  3. ....
  4. </xs:complexType>
  5. </xs:element>

De la mateixa manera que amb els tipus simples, es poden definir tipus complexos amb nom per reutilitzar-los com a tipus personalitzats.

  1. <xs:complexType name="curs">
  2. ...
  3. </xs:complexType>
  4.  
  5. <xs:element classe type="curs"/>
Atributs

Una característica bàsica d’XSD és que només els elements de tipus complex poden tenir atributs. En essència no hi ha gaires diferències entre definir un element o un atribut, ja que es fa de la mateixa manera però fent servir l’etiqueta attribute.

Els tipus de dades són els mateixos i, per tant, poden tenir tipus bàsics com en l’exemple següent:

  1. <xs:attribute name="número" type="xs:integer" />

S’hi poden posar restriccions de la mateixa manera que en els elements. En aquest exemple l’atribut any no pot tenir valors superiors a 2011 si es defineix d’aquesta manera:

  1. <xs:attribute name="any">
  2. <xs:simpleType>
  3. <xs:restriction base="xs:integer">
  4. <xs:maxInclusive value="2011"/>
  5. </xs:restriction>
  6. </xs:simpleType>
  7. </xs:attribute>

Tret que s’especifiqui el contrari, els atributs sempre són opcionals.

L’etiqueta <attribute> té una sèrie d’atributs que permeten definir característiques extra sobre els atributs (taula).

Taula: Atributs més importants de xs:attribute
Atribut Ús
use Permet especificar si l’atribut és obligatori (required), opcional (optional) o bé no es pot fer servir (prohibited).
default Defineix un valor per defecte.
fixed Es fa servir per definir valors obligatoris per als atributs.
form Permet definir si l’atribut ha d’anar amb l’àlies de l’espai de noms (qualified) o no (unqualified).

Per exemple, l’atribut any s’haurà d’especificar obligatòriament si es defineix de la manera següent:

  1. <xs:attribute name="any" type="xs:integer" use="required" />
'simpleContent'

Si l’element només conté text, el contingut de complexType serà un simpleContent. El simpleContent permet definir restriccions o extensions a elements que només tenen dades com a contingut.

La diferència més important és que en aquest cas es poden definir atributs en l’element. Els atributs s’afegeixen definint una extensió al tipus fet servir en l’element.

En aquest exemple l’element <Mida> té contingut de tipus enter i defineix dos atributs, llargada i amplada, que també són enters.

  1. <xs:complexType name="Mida">
  2. <xs:simpleContent>
  3. <xs:extension base="xs:integer">
  4. <xs:atribute name="llargada" type="xs:integer"/>
  5. <xs:atribute name="amplada" type="xs:integer"/>
  6. </xs:extension>
  7. </xs:simpleContent>
  8. </xs:complexType>
Contingut format per elements

Els elements que contenen altres elements també poden ser definits en XSD dins d’un <complexType> i poden ser alguns dels elements de la taula.

Taula: Elements per definir contingut complex
Etiqueta Serveix per
sequence Especificar el contingut com una llista ordenada d’elements.
choice Permet especificar elements alternatius.
all Definir el contingut com una llista desordenada d’elements.
complexContent Estendre o restringir el contingut complex.

L’element <sequence> és una de les maneres amb què el llenguatge XSD permet que s’especifiquin els elements que han de formar part del contingut d’un element. Fins i tot en el cas en què només hi hagi una sola etiqueta es pot definir com a una seqüència.

La condició més important que tenen és que els elements del document XML per validar han d’aparèixer en el mateix ordre en el qual es defineixen en la seqüència.

  1. <xs:element name="persona">
  2. <xs:complexContent>
  3. <xs:sequence>
  4. <xs:element name="nom" type="xs:string"/>
  5. <xs:element name="cognom" type="xs:string" maxOccurs="2"/>
  6. <xs:element name="tipus" type="xs:string" />
  7. </xs:sequence>
  8. </xs:complexContent>
  9. </xs:element>

En l’exemple anterior es defineix que abans de l’aparició de <tipus> poden aparèixer un o dos cognoms.

  1. <persona>
  2. <nom>Marcel</nom>
  3. <cognom>Puig</cognom>
  4. <cognom>Lozano</cognom>
  5. <tipus>Professor</tipus>
  6. </persona>

No validarà cap contingut si algun element no està exactament en el mateix ordre.

  1. <persona>
  2. <tipus>Professor</tipus>
  3. <cognom>Puig</cognom>
  4. <nom>Marcel</nom>
  5. </persona>

Les seqüències poden contenir dins seu altres seqüències d’elements.

  1. <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  2. <xs:element name="persona">
  3. <xs:complexType>
  4. <xs:sequence>
  5. <xs:element name="nomcomplet">
  6. <xs:complexType>
  7. <xs:sequence>
  8. <xs:element name="nom" type="xs:string"/>
  9. <xs:element name="cognom" type="xs:string" maxOccurs="2"/>
  10. </xs:sequence>
  11. </xs:complexType>
  12. </xs:element>
  13. <xs:element name="professió" type="xs:string"/>
  14. </xs:sequence>
  15. </xs:complexType>
  16. </xs:element>
  17. </xs:schema>

L’element <choice> serveix per fer que s’hagi de triar una de les alternatives de les que es presenten dins seu.

En aquest exemple l’element persona podrà contenir o bé l’etiqueta <nomCognoms> o bé <dni>, però no totes dues.

  1. <xs:complexType nom="persona">
  2. <xs:choice>
  3. <xs:element name="nomCognoms" type="xs:string"/>
  4. <xs:element name="dni" type="xs:string"/>
  5. </xs:choice>

Entre les alternatives hi pot haver seqüències o altres elements <choice>. La definició següent és un exemple més elaborat que l’anterior i permet que es pugui triar entre els elements <nom> i <cognom> o <dni>.

  1. <xs:choice>
  2. <xs:sequence>
  3. <xs:element name="nom" type="xs:string"/>
  4. <xs:element name="cognom" type="xs:string" maxOccurs="2"/>
  5. </xs:sequence>
  6. <xs:element name="dni" type="xs:string"/>
  7. </xs:choice>

La diferència més important entre l’element <all> i <sequence> és l’ordre. L’element <all> permet especificar una seqüència d’elements però permet que s’especifiquin en qualsevol ordre.

Per tant, si definim l’element <persona> de la manera següent:

  1. <xs:element name="persona">
  2. <xs:complexType>
  3. <xs:all>
  4. <xs:element name="nom"/>
  5. <xs:element name="cognom"/>
  6. </xs:all>
  7. </xs:complexType>
  8. </xs:element>

Ens servirà per validar tant aquest document:

  1. <persona>
  2. <nom>Pere</nom>
  3. <cognom>Garcia</nom>

com aquest:

  1. <persona>
  2. <cognom>Garcia</nom>
  3. <nom>Pere</nom>

Però sempre s’han de tenir en compte les limitacions d’aquest element que no eren presents en les seqüències ordenades:

  • Dins seu només hi pot haver elements. No hi pot haver ni seqüències, ni alternatives.
  • No es pot fer servir la cardinalitat en els elements que contingui, ja que provocaria un problema de no-determinisme.

Per tant, l’exemple següent no és correcte, ja que es demana que <cognom> pugui sortir dues vegades.

  1. <xs:all>
  2. <xs:element name="nom" type="xs:string"/>
  3. <xs:element name="cognom" maxOccurs="2" type="xs:string"/>
  4. </xs:all>

Una possible manera de permetre que es puguin especificar el nom i els dos cognoms en qualsevol ordre seria fer el següent:

  1. <xs:complexType>
  2. <xs:choice>
  3. <xs:sequence>
  4. <xs:element name="nom" type="xs:string"/>
  5. <xs:element name="cognom" type="xs:string" maxOccurs="2"/>
  6. </xs:sequence>
  7. <xs:sequence>
  8. <xs:element name="cognom" type="xs:string" maxOccurs="2"/>
  9. <xs:element name="nom" type="xs:string"/>
  10. </xs:sequence>
  11. </xs:choice>
  12. </xs:complexType>
'complexContent'

L’etiqueta complexContent permet definir extensions o restriccions a un tipus complex que contingui contingut barrejat o només elements.

Això fa que amb una extensió es pugui ampliar un contingut complex ja existent o restringir-ne els continguts.

Per exemple, si ja hi ha definit un tipus de dades nomComplet en què hi ha els elements <nom> i <cognom> se’n pot reutilitzar la definició per definir un nou tipus de dades, agenda, en què s’afegeixi l’adreça de correu electrònic.

  1. <xs:complexType name="nomComplet">
  2. <xs:sequence>
  3. <xs:element name="nom" type="xs:string"/>
  4. <xs:element name="cognom" type="xs:string" maxOccurs="2"/>
  5. </xs:sequence>
  6. </xs:complexType>
  7.  
  8. <xs:complexType name="agenda">
  9. <xs:complexContent>
  10. <xs:extension base="nomComplet">
  11. <xs:sequence>
  12. <xs:element name="email" type="xs:string" />
  13. </xs:sequence>
  14. </xs:extension>
  15. </xs:complexContent>
  16. </xs:complexType>

D’aquesta manera es podrà definir un element de tipus agenda:

  1. <xs:element name="persona" type="agenda"/>

que haurà de tenir els tres elements <nom>, <cognom>, i <email>:

  1. <persona>
  2. <nom>Pere</nom>
  3. <cognom>Garcia</cognom>
  4. <email>pgarcia@ioc.cat</email>
  5. </persona>
Elements sense contingut

Per a XSD els elements sense contingut són sempre de tipus complex. En la definició simplement no s’especifica cap contingut i tindrem un element buit.

  1. <xs:element name="delegat">
  2. <xs:complexType />
  3. </xs:element>

La definició permet definir l’element d’aquesta manera:

  1. <delegat />

Si l’element necessita atributs simplement s’especifiquen dins del complexType.

  1. <xs:element name="delegat">
  2. <xs:complexType>
  3. <xs:attribute name="any" use="required" type="xs:gYear"/>
  4. </xs:complexType>
  5. </xs:element>

I ja es podrà definir l’atribut en l’element buit:

  1. <delegat any="2012"/>
Contingut barrejat

Els elements amb contingut barrejat són els elements que tenen de contingut tant elements com text. Es va pensar per poder incloure elements enmig d’un text narratiu.

En XSD el contingut barrejat es defineix posant l’atribut mixed=“true” en la definició del l’element <complexType>.

  1. <xs:element name="carta">
  2. <xs:complexType mixed="true">
  3. <xs:sequence>
  4. <xs:element name="nom" type="xs:string"/>
  5. <xs:element name="dia" type="xs:gDay"/>
  6. </xs:sequence>
  7. </xs:complexType>
  8. </xs:element>

Això ens permetria validar un contingut com aquest:

  1. <carta>Estimat senyor <nom>Pere<nom>:
  2.  
  3. Li envio aquesta carta per recordar-li que hem quedat per
  4. trobar-nos el dia <dia>12</dia>
  5. </carta>

Exemple de creació d'un XSD

Es poden crear definicions de vocabularis XSD a partir de la idea del que volem que continguin les dades o bé a partir d’un fitxer XML de mostra.

Enunciat

En un centre en què només s’imparteixen els cicles formatius d’SMX i ASIX, quan han d’entrar les notes als alumnes ho fan creant un arxiu XML com el següent:

  1. <?xml version="1.0" ?>
  2. <classe modul="3" nommodul="Programació bàsica">
  3. <curs numero="1" especialitat="ASIX">
  4. <professor>
  5. <nom>Marcel</nom>
  6. <cognom>Puig</cognom>
  7. </professor>
  8. <alumnes>
  9. <alumne>
  10. <nom>Filomeno</nom>
  11. <cognom>Garcia</cognom>
  12. <nota>5</nota>
  13. </alumne>
  14. <alumne delegat="si">
  15. <nom>Frederic</nom>
  16. <cognom>Pi</cognom>
  17. <nota>3</nota>
  18. </alumne>
  19. <alumne>
  20. <nom>Manel</nom>
  21. <cognom>Puigdevall</cognom>
  22. <nota>8</nota>
  23. </alumne>
  24. </alumnes>
  25. </curs>
  26. </classe>

A la secretaria necessiten que es generi la definició del vocabulari en XSD per comprovar que els fitxers que reben són correctes.

Resolució

A l’hora de dissenyar un esquema des d’un XML sempre s’ha de partir d’un fitxer XML que tingui totes les opcions que poden sortir en cada element, o el resultat no serà vàlid per a tots els fitxers.

Una de les coses que s’han de tenir en compte a l’hora de fer un exercici com aquest és que no hi ha una manera única de fer-lo. Es pot fer amb tipus personalitzats, sense tipus personalitzats, a vegades es pot resoldre combinant uns elements però també amb uns altres, etc. Per tant, la solució que s’oferirà aquí és només una de les possibles solucions.

Una altra cosa que s’ha de tenir en compte és que els editors XML solen oferir una manera gràfica de crear XSD que permet crear esquemes de manera més senzilla, com es pot veure en la figura.

Figura Creació d’esquemes en mode disseny de l’oXygen XML Editor
Anàlisi

La primera fase normalment consisteix a analitzar el fitxer per determinar si hi ha parts que poden ser susceptibles de formar un tipus de dades. En l’exercici es pot veure que tant per al professor com per als alumnes les dades que contenen són semblants (els alumnes afegeixen la nota), i per tant es pot crear un tipus de dades que farà més simple el disseny final.

Per tant, en l’arrel podem crear el tipus complex persona, que contindrà el nom i el cognom.

  1. <xs:complexType name="persona">
  2. <xs:sequence>
  3. <xs:element name="nom" type="xs:string"/>
  4. <xs:element name="cognom" type="xs:string"/>
  5. </xs:sequence>
  6. </xs:complexType>

Com que els alumnes són iguals però amb un element extra es pot aprofitar el tipus persona estenent-lo.

  1. <xs:complexType name="estudiant">
  2. <xs:complexContent>
  3. <xs:extension base="persona">
  4. <xs:sequence>
  5. <xs:element name="nota">
  6. ...
  7. </xs:element>
  8. </xs:sequence>
  9. </xs:extension>
  10. </xs:complexContent>
  11. </xs:complexType>

Les notes no poden tenir tots els valors (només d’1 a 10), o sigui, que es pot afegir una restricció al tipus enter.

  1. <xs:element name="nota">
  2. <xs:simpleType>
  3. <xs:restriction base="xs:integer">
  4. <xs:maxInclusive value="10"/>
  5. <xs:minInclusive value="1"/>
  6. </xs:restriction>
  7. </xs:simpleType>
  8. </xs:element>
Desenvolupament

L’arrel sempre serà de tipus complex perquè conté atributs i tres elements, de manera que es pot començar la declaració com una seqüència d’elements.

  1. <xs:element name="classe">
  2. <xs:complexType>
  3. <xs:sequence>
  4. <xs:element name="curs">
  5. ...
  6. </xs:element>
  7. <xs:element name="professor" type="persona"/>
  8. <xs:element name="alumnes">
  9. ...
  10. </xs:element>
  11. </xs:sequence>
  12. </xs:complexType>
  13. </xs:element>

S’han deixat els elements <curs> i <alumnes> per desenvolupar-los, mentre que l’element <professor> ja es pot tancar perquè s’ha definit el tipus persona.

L’element <curs> no té contingut i, per tant, serà de tipus complex. A més, s’hi han de definir dos atributs.

  1. <xs:element name="curs">
  2. <xs:complexType>
  3. <xs:attribute name="numero" use="required">
  4. ...
  5. </xs:attribute>
  6. <xs:attribute name="especialitat" use="required">
  7. ...
  8. </xs:attribute>
  9. </xs:complexType>
  10. </xs:element>

Els atributs s’han de desenvolupar perquè no poden tenir tots els valors:

  • numero només pot ser “1” o “2”.
  • especialitat serà “SMX” o “ASIX”.

La manera més adequada per fer-ho serà restringir els valors amb una enumeració.

  1. <xs:attribute name="numero" use="required">
  2. <xs:simpleType>
  3. <xs:restriction base="xs:integer">
  4. <xs:enumeration value="1"/>
  5. <xs:enumeration value="2"/>
  6. </xs:restriction>
  7. </xs:simpleType>
  8. </xs:attribute>
  9. <xs:attribute name="especialitat" use="required">
  10. <xs:simpleType>
  11. <xs:restriction base="xs:string">
  12. <xs:enumeration value="ASIX"/>
  13. <xs:enumeration value="SMX"/>
  14. </xs:restriction>
  15. </xs:simpleType>
  16. </xs:attribute>

Ja només queda per desenvolupar <alumnes>, que té una repetició d’elements <alumne>, del qual ja n’hi ha un tipus.

  1. <xs:element name="alumne">
  2. <xs:complexType>
  3. <xs:sequence>
  4. <xs:element name="alumne" type="estudiant"
  5. maxOccurs="unbounded" minOccurs="1"/>
  6. </xs:sequence>
  7. </xs:complexType>
  8. </xs:element>

El resultat final serà:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  3.  
  4. <xs:complexType name="persona">
  5. <xs:sequence>
  6. <xs:element name="nom" type="xs:string"/>
  7. <xs:element name="cognom" type="xs:string"/>
  8. </xs:sequence>
  9. </xs:complexType>
  10.  
  11. <xs:complexType name="estudiant">
  12. <xs:complexContent>
  13. <xs:extension base="persona">
  14. <xs:sequence>
  15. <xs:element name="nota">
  16. <xs:simpleType>
  17. <xs:restriction base="xs:integer">
  18. <xs:maxInclusive value="10"/>
  19. <xs:minInclusive value="1"/>
  20. </xs:restriction>
  21. </xs:simpleType>
  22. </xs:element>
  23. </xs:sequence>
  24. <xs:attribute name="delegat" use="optional">
  25. <xs:simpleType>
  26. <xs:restriction base="xs:string">
  27. <xs:enumeration value="si"/>
  28. </xs:restriction>
  29. </xs:simpleType>
  30. </xs:attribute>
  31. </xs:extension>
  32. </xs:complexContent>
  33. </xs:complexType>
  34.  
  35. <xs:element name="classe">
  36. <xs:complexType>
  37. <xs:sequence>
  38. <xs:element name="curs">
  39. <xs:complexType>
  40. <xs:sequence>
  41. <xs:element name="professor" type="persona"/>
  42. <xs:element name="alumnes">
  43. <xs:complexType>
  44. <xs:sequence>
  45. <xs:element name="alumne" type="estudiant" maxOccurs="unbounded" />
  46. </xs:sequence>
  47. </xs:complexType>
  48. </xs:element>
  49. </xs:sequence>
  50. <xs:attribute name="numero" use="required">
  51. <xs:simpleType>
  52. <xs:restriction base="xs:integer">
  53. <xs:enumeration value="1"/>
  54. <xs:enumeration value="2"/>
  55. </xs:restriction>
  56. </xs:simpleType>
  57. </xs:attribute>
  58. <xs:attribute name="especialitat" use="required">
  59. <xs:simpleType>
  60. <xs:restriction base="xs:string">
  61. <xs:enumeration value="ASIX"/>
  62. <xs:enumeration value="SMX"/>
  63. </xs:restriction>
  64. </xs:simpleType>
  65. </xs:attribute>
  66. </xs:complexType>
  67. </xs:element>
  68. </xs:sequence>
  69. <xs:attribute name="modul" use="required" type="xs:integer" />
  70. <xs:attribute name="nommodul" use="required" type="xs:string" />
  71. </xs:complexType>
  72. </xs:element>
  73. </xs:schema>

Conversió entre esquemes

Una de les eines que s’han de tenir en compte a l’hora de fer esquemes és que ja hi ha eines que permeten convertir els llenguatges de definició d’esquemes d’un format a un altre.

A pesar d’això sempre s’hauran de revisar els resultats per comprovar que no s’ha perdut cap significat important en fer la conversió, ja que els sistemes automàtics no són perfectes.

La majoria dels editors XML porten alguna eina per fer la conversió (figura) però també hi ha eines específiques per fer conversions, com el programa de codi obert Trang.

Figura L’oXygen permet convertir els esquemes d’un format a un altre

Trang

El Trang és un programa de codi obert que permet convertir les definicions de vocabularis fetes en un sistema a un altre. Pot convertir els llenguatges següents:

  • RELAX NG (XML syntax)
  • RELAX NG compact syntax
  • XML 1.0 DTD
  • W3C XML Schema

Per fer la conversió simplement hem d’especificar el fitxer que es vol convertir i en què es vol convertir. L’única condició és que l’esquema d’origen no sigui un XML Schema.

Es pot convertir fent servir fitxers que tinguin les extensions per defecte: DTD (.dtd), XML Schemas (.xsd), RELAX NG (.rng), RELAX NG compact (.rnc).

Amb això es crearà alumnes.xsd a partir d‘alumnes.dtd.

  1. $ trang alumnes.dtd alumnes.xsd

O bé especificant explícitament quin és el tipus de fitxer d’entrada (-I) i quin el de sortida (-O).

  1. $ trang -I dtd -O xsd alumnes.dtd alumnes.xsd

Una de les característiques interessants que té és que pot crear el vocabulari basant-se en fitxers XML de mostra. A partir del fitxer XML, alumnes.xml, es pot fer que el Trang generi automàticament l’XML Schema que el validaria executant:

  1. $ trang alumnes.xml alumnes.xsd

Si es tenen diversos documents XML també pot generar l’esquema associat amb aquests:

  1. $ trang *.xml alumnes.xsd
Anar a la pàgina anterior:
Exercicis d'autoavaluació
Anar a la pàgina següent:
Activitats