Les schémas du W3C
Introduction
Les schémas XML
♦ Objectifs : étendre le système des DTDs en introduisant
- des types plus complexes,
- séparation des types et de la structure,
- une approche objet (héritage),
- une syntaxe XML.
♦ La version 1.0 est proposé par le consortium W3C en 2001 et une révision en 2004.
♦ La solution proposée est complexe.
♦ Tous les analyseurs ne traitent pas les schémas.
Espace de noms et schéma XML
♦ Un exemple de schéma :
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.univmed.fr/massat" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0" > ... </xs:schema>
Dans cette définition, le schéma est associé à un espace de noms. Il est sensé définir tous les éléments de cet espace.
Associer un schéma et un document
♦ Le document XML :
... <stock xmlns="http://www.univmed.fr/massat" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > ... </stock> ...
L'élément stock n'est pas forcément la racine du document XML.
♦ On peut ajouter :
xsi:schemaLocation="
http://www.univmed.fr/massat file:///lib/schemas/test.xsd
espace_de_noms_1 url_de_schema_1
espace_de_noms_2 url_de_schema_2
..."
Schéma sans espace de noms
♦ Un exemple sans espace de noms :
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" > ... </xs:schema>
♦ Le document XML :
<stock xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test.xsd" > ... </stock>
Hiérarchie des types
Les schémas distinguent plusieurs types :
- Types simples
(#PCDATA ou feuille de l'arbre)
- Types de base ou primitifs (entier, chaîne, ...)
- Types simples
définis :
- par restriction d'un type simple,
- par extension d'un type simple.
- Types complexes
(noeuds de l'arbre) définis
- par construction,
- par extension d'un type simple,
- par extension d'un type complexe,
- par restriction d'un type complexe.
Les types de base
Les chaînes et les données
Les chaînes :
- xs:string toute chaîne de caractères
- xs:normalizedString suite de blancs compactée
- xs:token idem plus les blancs devant et derrière
Les données binaires :
- xs:base64Binary
- xs:hexBinary
L'espace lexical désigne la forme sérialisée (fichier XML) et l'espace des valeurs désigne la forme lue (arbre en mémoire). Pour une même valeur il existe plusieurs représentations lexicales.
Les décimaux et les durées
Les décimaux :
- xs:decimal
- xs:float et xs:double avec les infinis et les NaN
Date et durée:
- xs:time (HH:MM:SS.sss)
- xs:date (YYYY-MM-DD)
- xs:dateTime (dateTtime)
Les entiers
Taille non fixée :
- xs:integer
- xs:positiveInteger
- xs:negativeInteger
- xs:nonNegativeInteger
- xs:nonPositiveInteger
Taille définie :
- xs:int et xs:unsignedInt (32 bits)
- xs:long et xs:unsignedLong (64 bits)
- xs:short et xs:unsignedShort (16 bits)
- xs:byte et xs:unsignedByte (8 bits)
- xs:boolean (true, false, 0, 1)
Les noms
Les noms:
- xs:Name nom simple
- xs:NCName nom simple sans « : »
- xs:QName nom qualifié
- xs:anyURI
Les clefs et les références:
- xs:ID
- xs:IDREF
- xs:IDREFS
les données de DTD:
- xs:ENTITY
- xs:ENTITIES
- xs:NOTATION
- xs:NMTOKEN
- xs:NMTOKENS
Hiérarchie des types de base
xs:anyType | xs:anySimpleType | | xs:string | | | xs:normalizedString | | | | xs:token | | | | | xs:name | | | | | | xs:NCName | | | | | | | xs:ID | | | | | | | xs:IDREF | | | | | | | | xs:IDREFS | | | | | | | xs:ENTITY | | | | | | | | xs:ENTITIES | | | | | xs:NMTOKEN | | | | | | xs:NMTOKENS | | | | | xs:langage | | xs:decimal | | | xs:integer | | | | xs:nonPositiveInteger | | | | | xs:NegativeInteger | | | | xs:nonNegativeInteger | | | | | xs:PositiveInteger | | | | ... les longs ... | | ... les durées ...
Types simples et facettes
Les types simples par restriction
♦ Un type simple (#PCDATA) provient de la restriction d'un autre type simple par application d'une facette :
<xs:simpleType name="poids">
<xs:restriction base="xs:int">
<!-- facette -->
<xs:minInclusive value='100'/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="poidsMoyen">
<xs:restriction base="poids">
<xs:maxExclusive value='200'/>
</xs:restriction>
</xs:simpleType>
Facette de définition des bornes
♦ Les facettes sont des restrictions de types simples:
<xs:minInclusive value='valeur'/> <xs:minExclusive value='valeur'/> <xs:maxInclusive value='valeur'/> <xs:maxExclusive value='valeur'/>
L'interprétation des bornes varie en fonction du type de base.
Liste de valeurs acceptables
♦ Une énumération est possible
<xs:restriction base="xs:string"> <xs:enumeration value='rouge'/> <xs:enumeration value='noir'/> <xs:enumeration value='bleu'/> </xs:restriction>
Cette énumération doit être compatible avec le type de base.
Motif et longueur
♦ on peut agir sur la forme:
<xs:restriction base="xs:string"> <xs:pattern value='[dD][iI}[lL].*'/> <xs:maxLength value='20'/> <xs:minLength value='5'/> </xs:restriction>
La facette xs:length est aussi disponible.
<xs:restriction base="xs:float"> <xs:totalDigits value='8'/> <xs:fractionDigits value='5'/> </xs:restriction>
Gestion des blancs
♦ finalement, nous pouvons contrôler les blancs:
<xs:restriction base="xs:string"> <xs:whiteSpace value='collapse'/> <xs:minLength value='5'/> </xs:restriction>
♦ replace change les tabulations et les retours chariot en blancs.
♦ preserve n'altère pas les blancs.
Types simples par extension
♦ Liste de valeurs:
<xs:simpleType name="listeAges"> <xs:list itemType="age"/> </xs:simpleType>
Une exemple de texte valide :
10 34 67
♦ Union de valeurs:
<xs:simpleType name="ageInconnu">
<xs:union memberTypes="age ">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value='inconnu'/>
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
Les types simples sont spécifiés par leur nom (attribut memberTypes) ou leur définition (éléments xs:simpleType).
Un type peut être global et nommé pour une utilisation future ou créer localement en fonction des besoins.
Définir des éléments
Définition des éléments (xs:element)
♦ Définir un élément à partir d'un type :
<xs:element name="prix" type="typePrix" />
ou directement :
<xs:element name="prix">
... définition locale du type ...
</xs:element>
Les attributs font partie du type.
Définition des éléments (2)
Définition à partir d'un type local :
<xs:element
id = "xs:ID(?)"
abstract = "xs:boolean(false)"
default = "value(?)"
fixed = "xs:string(?)"
name = "xs:NCName(?)"
form = "unqualified|qualified(unqualified)"
...
>
(xs:annotation?)
(xs:simpleType|xs:complexType)?
</xs:element>
Référence à une autre définition :
<xs:element
ref = "xs:QName(?)"
...
/>
Définition locale ou globale
Si la définition est locale (nichée dans un type complexe) on peut utiliser des attributs de cardinalités :
...
<xs:element
minOccurs = "xs:nonNegativeInteger(1)"
maxOccurs = "xs:nonNegativeInteger|unbounded(1)"
name = "xs:NCName(?)"
...
>
...
</xs:element>
...
Définition d'un attribut (xs:attribute)
♦ Définir un attribut et son type :
<xs:attribute
name="xs:NCName"
id="xs:ID(?)"
default="value(?)"
fixed="xs:string(?)"
use="optional|prohibited|required(optional)"
...
>
(xs:annotation)?
(xs:simpleType)
</xs:attribute>
La définition peut être locale ou globale.
♦ On peut également faire référence à un type simple déjà défini :
<xs:attribute
name="xs:NCName"
type="xs:QName"
... autres attributs optionnels ...
>
(xs:annotation)?
</xs:attribute>
Cette définition peut également être locale ou globale.
Référence à un attribut (xs:attribute)
♦ Faire référence à un attribut déjà défini:
<xs:attribute
ref="xs:QName"
... autres attributs optionnels ...
/>
La référence doit être locale (nichée dans un type complexe).
Attributs libres (xs:anyAttribute)
♦ Déclaration générique :
<xs:anyAttribute
id = "xs:ID(?)"
namespace = (
( "##any" | "##other" ) |
liste de ( xs:anyURI | "##targetNamespace" | "##local" )))
processContents="skip|lax|strict(strict)"
...
>
(xs:annotation)?
</xs:anyAttribute>
Groupe d'attributs (xs:attributeGroup)
♦ Déclaration :
<xs:attributeGroup
id = "xs:ID(?)"
name = "xs:NCName"
...
>
(xs:annotation)?
(xs:attribute|xs:attributeGroup)*
(xs:anyAttribute)?
</xs:attributeGroup>
Utilisation :
<xs:attributeGroup
ref = "xs:NCName"
/>
Types complexes
Définition d'un T.C. (xs:complexType)
♦ Un contenu complexe peut contenir des sous-éléments des attributs ou les deux.
<xs:complexType abstract = "xs:boolean(false)" mixed = "xs:boolean(false)" id = "xs:ID(?)" name = "xs:NCName" ... > (xs:annotation?) (xs:group|xs:all|xs:choice|xs:sequence)? (xs:attribute|xs:attributeGroup)* (xs:anyAttribute?) </xs:complexType>
L'attribut mixed indique si l'on peut mélanger du texte et des éléments.
Les séquences (xs:sequence)
♦ L'élément xs:sequence permet d'indiquer un contenu ordonné et éventuellement répété.
<xs:sequence id="xs:ID(?)" minOccurs="xs:nonNegativeInteger(1)" maxOccurs="xs:nonNegativeInteger|unbounded(1)" > (xs:annotation?) (xs:element|xs:group|xs:choice|xs:sequence|xs:any)* </xs:sequence>
Les choix (xs:choice)
♦ L'élément xs:choice permet d'exprimer un contenu alternatif et éventuellement répété.
<xs:choice id="xs:ID(?)" minOccurs="xs:nonNegativeInteger(1)" maxOccurs="xs:nonNegativeInteger|unbounded(1)" > (xs:annotation?) (xs:element|xs:group|xs:choice|xs:sequence|xs:any)* </xs:choice>
Éléments sans ordre (xs:all)
♦ L'élément xs:all permet d'indiquer une suite d'éléments sans préciser l'ordre d'apparition.
<xs:all id="xs:ID(?)" minOccurs="0|1(1)" maxOccurs="1" > (xs:annotation?) (xs:element*) </xs:all>
Groupage (xs:group)
♦ L'élément xs:group permet de regrouper un contenu complexe.
<xs:group name="xs:NCName(?)" minOccurs="xs:nonNegativeInteger(1)" maxOccurs="xs:nonNegativeInteger|unbounded(1)" > (xs:annotation?) (xs:all|xs:choice|xs:sequence) </xs:group>
ou
<xs:group ref="xs:NCName" ... > ... </xs:group>
Contenu libre (xs:any)
♦ Le modèle libre avec xs:any
<xs:any
id="xs:ID(?)"
minOccurs="xs:nonNegativeInteger(1)"
maxOccurs="xs:nonNegativeInteger|unbounded(1)"
namespace="##any | ##other | xs:anyURI*
##targetNamespace? ##local?"
processContents="lax|skip|strict(strict)"
>
(xs:annotation?)
</xs:any>
- ##any : tous les éléments
- ##other : éléments hors de l'espace de noms cible
- ##targetNamespace : éléments de l'espace de noms cible
- ##local : éléments sans espace de noms
- strict : déclaration et validation,
- skip : pas de validation,
- lax : validation si déclaration,
Espace de noms
♦ Un schéma qui utilise les espaces de noms et impose une qualification par défaut des éléments :
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.univmed.fr/massat"
elementFormDefault="qualified" >
<xs:element name="personne" >
<xs:complexType>
<xs:sequence>
<xs:element name="nom" type="xs:string" />
<xs:element name="prenom" form="unqualified" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Un document valide pour ce schéma :
<?xml version="1.0" encoding="iso-8859-1" ?> <mas:personne xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mas="http://www.univmed.fr/massat" xsi:schemaLocation="http://www.univmed.fr/massat mon-schema.xsd" > <mas:nom> Simon </mas:nom> <prenom> Alfred </prenom> </mas:personne>
Type complexe par dérivation
Extension d'un type simple
♦ Définition d'un contenu complexe à partir d'un type simple par extension :
<xs:complexType ... >
(xs:annotation?)
<xs:simpleContent>
(xs:annotation?)
<xs:extension base="xs:anySimpleType">
(xs:attribute|xs:attributeGroup)*
(xs:anyAttribute?)
</xs:extension>
</xs:simpleContent>
</xs:complexType>
Pour étendre un type simple et le transformer en type complexe nous devons obligatoirement lui ajouter au moins un attribut.
Un petit exemple :
<!-- CT001 définition d'un type complexe par extension
d'un type simple avec ajout d'attributs -->
<xs:complexType name="produit">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="prix" type="xs:float" use="required"/>
<xs:anyAttribute processContents="lax"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:element name="produit" type="produit"/>
document XML valide :
<produit prix="100.67" comment="trop chère"> Voiture </produit>
Extension d'un type complexe
♦ Extension d'un type complexe:
<xs:complexType ... >
(xs:annotation?)
<xs:complexContent mixed="xs:boolean(false)" >
(xs:annotation?)
<xs:extension id="xs:ID(?)" base="xs:QName" >
(xs:annotation?)
(xs:group|xs:all|xs:choice|xs:sequence)?
(xs:attribute|xs:attributeGroup)*
(xs:anyAttribute?)
</xs:extension>
</xs:complexContent>
</xs:complexType>
La valeur de l'attribut mixed doit être cohérente avec celle définie dans le type complexe.
Exemple. Partons du type complexe personne qui est une séquence d'éléments :
<!-- CT000 définition d'un type complexe
séquence d'éléments -->
<xs:complexType name="personne">
<xs:sequence>
<xs:element name="nom" type="xs:string" />
<xs:element name="prenom" type="xs:string" minOccurs="0" maxOccurs="5"/>
<xs:element name="age" type="xs:integer" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
Nous pouvons définir une personne avec adresse :
<!-- CT003 Type complexe par extension
d'un type complexe -->
<xs:complexType name="personneAvecAdresse">
<xs:complexContent>
<xs:extension base="personne">
<xs:sequence>
<xs:element name="adresse" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="personneAvecAdresse" type="personneAvecAdresse"/>
document XML valide:
<personneAvecAdresse id="j.martin"> <nom>Martin</nom> <prenom>Jean</prenom> <prenom>Pierre</prenom> <adresse>10, bd de la Lune, 54321 Marseille</adresse> </personneAvecAdresse>
Restriction d'un type complexe
♦ Définition d'un type complexe par restriction d'un type complexe:
<xs:complexType ... >
(xs:annotation?)
<xs:complexContent mixed="xs:boolean(false)" >
(xs:annotation?)
<xs:restriction id="xs:ID(?)" base="xs:QName" >
(xs:annotation?)
(xs:group|xs:all|xs:choice|xs:sequence)?
(xs:attribute|xs:attributeGroup)*
(xs:anyAttribute?)
</xs:restriction>
</xs:complexContent>
</xs:complexType>
La valeur de l'attribut mixed doit être cohérente avec celle définie dans le type complexe.
Exemple. En partant de la personne avec adresse nous allons définir une personne avec adresse mais sans age et au plus un prénom :
<!-- CT004 définition d'un type complexe par restriction
d'un type complexe -->
<xs:complexType name="personneAvecAdresseSansAge">
<xs:complexContent>
<xs:restriction base="personneAvecAdresse">
<xs:sequence>
<xs:element name="nom" type="xs:string" />
<!-- restriction sur la cardinalité -->
<xs:element name="prenom" type="xs:string"
minOccurs="0" maxOccurs="1"/>
<!-- l'age est supprimé -->
<xs:element name="adresse" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
Document XML valide :
<personneAvecAdresseSansAge id="p.martin"> <nom>Martin</nom> <prenom>Pierre</prenom> <adresse>10, bd de la Lune, 54321 Marseille</adresse> </personneAvecAdresseSansAge>
Absence de valeur
Dans un élément XML, l'absence de valeur peut être explicitement représentée par l'attribut xsi:nil afin d'éviter la confusion avec la chaîne vide.
<prix xsi:nil="true" />
A condition que le schéma prévoit cette possibilité :
<xs:element name="prix" type="xs:decimal" nillable=true" />
Clef et référence
Les schémas offrent la possibilité de définir avec une grande précision des clefs (avec xs:unique ou xs:key) et des références (avec xs:keyref).
<xs:complexType name="liste-de-produits">
<xs:sequence>
<xs:element name="produit" maxOccurs="unbounded">
<xs:complexType >
<xs:sequence>
<xs:element name="code" type="xs:string" />
<xs:element name="nom" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ref-produit" maxOccurs="unbounded">
<xs:complexType >
<xs:sequence>
<xs:element name="code" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
Utilisation du type complexe et définition des clefs :
<xs:element name="liste-de-produits" type="liste-de-produits">
<!-- définition d'un index -->
<xs:unique name="produit.code">
<xs:selector xpath="produit"/> <!-- les éléments indexés -->
<xs:field xpath="code"/> <!-- la clef -->
</xs:unique>
<!-- utilisation de cet index -->
<xs:keyref name="ref.produit.code" refer="produit.code">
<xs:selector xpath="ref-produit"/>
<xs:field xpath="code"/>
</xs:keyref>
</xs:element>
Les clefs composites sont supportées, il suffit de placer plusieurs clauses xs:field. L'élément xs:key est identique à xs:unique mais il impose en plus la présence obligatoire de la clef.
et voila un exemple d'élément XML valide pour ce schéma :
...
<liste-de-produits>
<!-- liste des produits -->
<produit>
<code>AZD100</code>
<nom>Baladeur MP3</nom>
</produit>
<produit>
<code>AZD102</code>
<nom>Baladeur MP3</nom>
</produit>
<!-- références aux produits -->
<ref-produit>
<code>AZD100</code>
</ref-produit>
</liste-de-produits>
...
La contrainte s'applique sur un type complexe, donc sur un sous-arbre du document XML.