Les schémas du W3C

Introduction

Les schémas XML

♦ Objectifs : étendre le système des DTDs en introduisant

♦ 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 :

Les types de base

Les chaînes et les données

Les chaînes :

Les données binaires :

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 :

Date et durée:

Les entiers

Taille non fixée :

Taille définie :

Les noms

Les noms:

Les clefs et les références:

les données de DTD:

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>

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.