Valid XHTML     Valid CSS2    

XML en L2, université d'Angers

    gilles.hunault@univ-angers.fr

 

T.P. numéro 3

 

Table des matières cliquable

  1. Créer un document XML qui respecte une grammaire DTD

  2. Créer un document XML qui respecte une grammaire XSD

  3. DTD et paramètres

  4. Validité suivant la grammaire

  5. Créer des grammaires DTD et XSD

  6. Non unicité des grammaires DTD et XSD

  7. Vérification des vérificateurs

  8. Rajout de contraintes

 

Il est possible d'afficher toutes les solutions via  ?solutions=1  et de les masquer via  ?solutions=0 .

 

1. Créer un document XML qui respecte une grammaire DTD

Nous admettrons pour ce qui suit qu'une molécule est définie par une liste d'atomes avec le nom des atomes, leur notation et le nombre de ces atomes fournis en attributs. Par exemple la molécule d'eau H2O est définie par l'atome de nom hydrogène, de notation H en nombre 2 et par l'atome de nom oxygène, de notation O et en nombre 1 (qui peut donc être omis). Après avoir lu soigneusement le fichier suivant nommé molecule.dtd, écrire un fichier eau.xml valide pour cette grammaire DTD. On utilisera un outil en ligne de commande pour vérifier cette validité. On pourra ignorer les accents.

Fichier molecule.dtd :


     <!ELEMENT molecule (atome)+ >
     <!ELEMENT atome    EMPTY    >
     
     <!ATTLIST atome    nom      CDATA  #REQUIRED >
     <!ATTLIST atome    lettre   CDATA  #REQUIRED >
     <!ATTLIST atome    nombre   CDATA  "1"       >
     

Est-ce que cette grammaire DTD autorise les chaines vides pour les noms, lettres et nombres ?

Est-ce qu'il faut respecter l'ordre des attributs tel qu'il est défini dans la grammaire DTD ?

Solution :  

Voici un fichier eau.xml possible :


     <?xml version="1.0" ?>
     <molecule>
       <atome nom="Hydrogene" lettre="H" nombre="2" />
       <atome nom="Oxygene"   lettre="O" nombre="1" />
     </molecule>
     

On peut vérifier la validité de ce fichier pour la grammaire molecule.dtd via les commandes suivantes :


     xmllint --dtdvalid molecule.dtd eau.xml
     
     xmlstarlet val -d molecule.dtd eau.xml
     
     xmlstarlet val -e -d molecule.dtd eau.xml # s'il y a es erreurs
     

Hélas, oui cette grammaire DTD autorise les chaines vides pour les noms, lettres et les nombres car CDATA, comme #PCDATA inclut la chaine vide. On pourra s'en rendre compte avec le fichier suivant eauvide.xml qui est valide pour cette grammaire. Attention : une chaine de caractères réduite à un espace est aussi valide, mais vraiment peu informative...

De la même façon, l'ordre des attributs est libre, à condition qu'un attribut n'apparaisse pas deux fois, comme le montre le fichier suivant, qui est valide pour la DTD considérée :

Fichier eaudtdatt.xml :


     <?xml version="1.0" ?>
     <!DOCTYPE molecule SYSTEM "molecule.dtd">
     <molecule>
       <atome lettre=""  nom=""  nombre="" />
       <atome nombre=""  nom=""  lettre="" />
     </molecule>
     

Voici le message d'erreur en cas de duplication d'attribut, comme c'est le cas pour le fichier eaudtdatt2.xml :


     <?xml version="1.0" ?>
     <!-- fichier  eaudtdatt2.xml -->
     <!DOCTYPE molecule SYSTEM "molecule.dtd">
     <molecule>
       <atome lettre=""  nom=""  nombre="" />
       <atome nombre=""  nom=""  lettre="" nombre = "" />
     </molecule>
     

     $gh> xmlstarlet val -e -d molecule.dtd eaudtdatt2.xml
     
     eaudtdatt2.xml:5.51: Attribute nombre redefined
     
     <atome nombre=""  nom=""  lettre="" nombre = "" />
                                                       ^
     eaudtdatt2.xml - invalid
     

 

2. Créer un document XML qui respecte une grammaire XSD

On décide maintenant d'utiliser la grammaire molecule.xsd suivante pour stocker les mêmes informations.

Fichier molecule.xsd :


     <?xml version="1.0" encoding="UTF-8"?>
     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     
       <xsd:element name="molecule">
         <xsd:complexType>
           <xsd:sequence>
             <xsd:element maxOccurs="unbounded" ref="atome"/>
           </xsd:sequence>
         </xsd:complexType>
       </xsd:element>
     
       <xsd:element name="atome">
         <xsd:complexType>
           <xsd:sequence>
             <xsd:element ref="nom"/>
             <xsd:element ref="nombre"/>
           </xsd:sequence>
         </xsd:complexType>
       </xsd:element>
     
       <xsd:element name="nom">
         <xsd:complexType>
            <xsd:simpleContent>
               <xsd:extension base="xsd:string">
                  <xsd:attribute name="lettre" use="required" type="xsd:string"/>
               </xsd:extension>
            </xsd:simpleContent>
         </xsd:complexType>
       </xsd:element>
     
       <xsd:element name="nombre" type="xsd:integer"/>
     
     </xsd:schema>
     

Ecrire un fichier eau2.xml valide pour cette grammaire XSD. On utilisera un outil en ligne de commande pour vérifier cette validité. Là encore, on pourra ignorer les accents.

Est-ce que cette grammaire XSD autorise les chaines vides pour les noms, lettres et nombres ?

Solution :  

Voici un fichier eau2.xml possible :


     <?xml version="1.0" ?>
     <molecule>
       <atome>
          <nom lettre="O">Oxygene</nom>
          <nombre>1</nombre>
       </atome>
       <atome>
          <nom lettre="H">Hydrogene</nom>
          <nombre>2</nombre>
       </atome>
     </molecule>
     

On peut vérifier la validité de ce fichier pour la grammaire molecule.xsd via les commandes suivantes :


     xmllint --noout --schema molecule.xsd eau2.xml
     
     xmlstarlet val -s molecule.xsd eau2.xml
     

Hélas, oui cette grammaire XSD autorise les chaines vides pour les noms, lettres, mais pas pour les nombres car xsd:string inclut la chaine vide mais xsd:integer l'interdit. On pourra s'en rendre compte avec le fichier suivant eau3.xml qui est valide pour cette grammaire. Attention : ici aussi une chaine de caractères réduite à un espace est valide, mais vraiment peu informative...

 

3. DTD et paramètres

On structure un dessin 2D élémentaire selon les règles suivantes. Un dessin est un cercle, un rectangle ou un triangle. Un cercle est défini par un centre et un rayon, un rectangle par son csg (coin supérieur gauche), une longueur et une largeur, un triangle par trois points. Chaque élément graphique peut avoir une couleur parmi les mots prédéfinis bleu, rouge, jaune ou vert. Ecrire un fichier dessin.xml qui dans un viewport bleu de 500 pixels de large et 300 de haut, met un "soleil" jaune de rayon 15 pixels en (450,50), une plage rectangulaire verte qui fait toute la largeur et qui fait 100 pixels de haut. Pour les plus fort(e)s, rajouter une petite étoile de mer rouge en bas à gauche à l'aide de trois triangles bien choisis. A défaut, on placera juste un triangle plein et en rouge. On pourra s'inspirer du dessin ci-dessous et de la syntaxe SVG.

                              non su

Ecrire ensuite la "vraie" version en SVG de ce dessin.

Comment vérifier que ce dessin est valide au sens de la grammaire de SVG ?

Quel logiciel, du type paint, gimp ou photoshop, permet de lire et d'écrire des fichiers SVG ?

Donner les grammaires DTD et XSD associées au fichier dessinTP.xml. On nommera dessin.dtd et dessin.xsd les fichiers-grammaires correspondants. On essaiera de paramétrer l'attribut couleur qui est commun.

Solution :  

Voici un fichier XML possible pour le dessin avec une syntaxe en français adaptée de SVG, nommé dessin.xml


     <?xml version="1.0" encoding="ISO-8859-1" ?>
     <dessins largeur="500px" hauteur="300px">
     <dessin>
     
       <!-- tout en bleu ciel -->
       <rectangle  xcsg="0"    ycsg="0"   largeur="500" hauteur="300"   couleur="bleu-ciel"   />
     
       <!-- le soleil en jaune -->
       <cercle xcentre="450" ycentre="50"  rayon="15"  couleur="jaune" />
     
       <!-- la plage en vert -->
       <rectangle xcsg="0"    ycsg="200"  largeur="500" hauteur="100" couleur="vert"   />
     
       <!-- un triangle rouge -->
       <triangle couleur="rouge" xpoint1="50" ypoint1="250" xpoint2="75" ypoint2="225" xpoint3="100" ypoint3="250" />
     
     </dessin>
     </dessins>
     

La "vraie" version dont un rendu est dans dessin1.svg est la suivante


     <?xml version="1.0" encoding="ISO-8859-1" ?>
     <svg xmlns="http://www.w3.org/2000/svg" width="500px" height="300px">
     <g>
     
       <!-- tout en bleu ciel -->
       <rect    x="0"    y="0"   width="500" height="300"   fill="cyan"   />
     
       <!-- le soleil en jaune -->
       <circle cx="450" cy="50"  r="15"                     fill="yellow" />
     
       <!-- la plage en vert -->
       <rect    x="0"    y="200"   width="500" height="100" fill="green"   />
     
       <!-- un triangle rouge -->
       <path fill="red" d="M 50,250 L 75,225 L 100, 250 z" />
     
     </g>
     </svg>
     

Et pour le dessin avec l'étoile dessin2.svg on peut utiliser


     <?xml version="1.0" encoding="ISO-8859-1" ?>
     <!DOCTYPE svg SYSTEM "Dtd/svg.dtd">
     <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500px" height="300px">
     <g>
     
       <!-- tout en bleu ciel -->
       <rect    x="0"    y="0"   width="500" height="300"   fill="cyan"   />
     
       <!-- le soleil en jaune -->
       <circle cx="450" cy="50"  r="15"                     fill="yellow" />
     
       <!-- la plage en vert -->
       <rect    x="0"    y="200"   width="500" height="100" fill="green"   />
     
       <!-- l'étoile de mer en rouge avec 2 triangles rouges et un triangle vert -->
     
       <path fill="red"   d="M 050,250 L 080,265 L 110,250 z" />
       <path fill="red"   d="M 065,285 L 080,225 L 095,285 z" />
       <path fill="green" d="M 065,285 L 080,265 L 095,285 z" />
     
     </g>
     </svg>
     

Le W3C avec son fameux Markup Validation Service est capable de tester si ce fichier est valide pour la grammaire de SVG.

Au niveau logiciel, c'est sans doute Inkscape qui est le plus complet et le plus agréale à utiliser pour dessiner, lire et écrire des images au format SVG.

Rendu SVG (si le browser le permet)
Rendu PNG (copie d'écran) dessin1 dessin2

Là encore, pour dessin.xml, il y a peu d'éléments et d'attributs dans dessin.xml donc une grammaire DTD «vite fait» peut être dessin.dtd dont le contenu est :


     <!-- dessin.dtd -->
     
     <!-- une grammaire élémentaire pour dessin.xml -->
     
     <!ELEMENT dessins   (dessin+)  >
     <!ELEMENT dessin    (cercle|rectangle|triangle)* >
     <!ELEMENT cercle    EMPTY >
     <!ELEMENT rectangle EMPTY>
     <!ELEMENT triangle  EMPTY>
     
     <!ATTLIST dessins largeur CDATA "REQUIRED">
     <!ATTLIST dessins hauteur CDATA "REQUIRED">
     
     <!ATTLIST cercle xcentre CDATA "REQUIRED">
     <!ATTLIST cercle ycentre CDATA "REQUIRED">
     <!ATTLIST cercle rayon   CDATA "REQUIRED">
     <!ATTLIST cercle couleur CDATA "REQUIRED">
     
     <!ATTLIST rectangle xcsg    CDATA "REQUIRED">
     <!ATTLIST rectangle ycsg    CDATA "REQUIRED">
     <!ATTLIST rectangle largeur CDATA "REQUIRED">
     <!ATTLIST rectangle hauteur CDATA "REQUIRED">
     <!ATTLIST rectangle couleur CDATA "REQUIRED">
     
     <!ATTLIST triangle couleur CDATA "REQUIRED">
     <!ATTLIST triangle xpoint1 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint1 CDATA "REQUIRED">
     <!ATTLIST triangle xpoint2 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint2 CDATA "REQUIRED">
     <!ATTLIST triangle xpoint3 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint3 CDATA "REQUIRED">
     

Pour forcer les couleurs à ne prendre que les valeurs autorisées, on peut utiliser une liste de valeurs, soit :


     <!-- dessin2.dtd -->
     
     <!-- une autre grammaire pour dessin.xml -->
     
     <!ELEMENT dessins   (dessin+)  >
     <!ELEMENT dessin    (cercle|rectangle|triangle)* >
     <!ELEMENT cercle    EMPTY >
     <!ELEMENT rectangle EMPTY>
     <!ELEMENT triangle  EMPTY>
     
     <!ATTLIST dessins largeur CDATA "REQUIRED">
     <!ATTLIST dessins hauteur CDATA "REQUIRED">
     
     <!ATTLIST cercle couleur (bleu|jaune|rouge|vert) "bleu" >
     <!ATTLIST cercle xcentre CDATA "REQUIRED">
     <!ATTLIST cercle ycentre CDATA "REQUIRED">
     <!ATTLIST cercle rayon   CDATA "REQUIRED">
     
     <!ATTLIST rectangle couleur (bleu|jaune|rouge|vert) "bleu" >
     <!ATTLIST rectangle xcsg    CDATA "REQUIRED">
     <!ATTLIST rectangle ycsg    CDATA "REQUIRED">
     <!ATTLIST rectangle largeur CDATA "REQUIRED">
     <!ATTLIST rectangle hauteur CDATA "REQUIRED">
     
     <!ATTLIST triangle couleur (bleu|jaune|rouge|vert) "bleu" >
     <!ATTLIST triangle xpoint1 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint1 CDATA "REQUIRED">
     <!ATTLIST triangle xpoint2 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint2 CDATA "REQUIRED">
     <!ATTLIST triangle xpoint3 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint3 CDATA "REQUIRED">
     

Pour éviter la duplication par copier/coller de cette liste des couleurs, le mieux est d'utiliser une entité paramétrique, qui fonctionne comme une entité HTML mais qui commence par le symbole % (pourcent) :


     <!-- dessin3.dtd -->
     
     <!-- encore une autre grammaire pour dessin.xml -->
     
     <!ENTITY % couleur "(bleu|jaune|rouge|vert)" >
     
     <!ELEMENT dessins   (dessin+)  >
     <!ELEMENT dessin    (cercle|rectangle|triangle)* >
     <!ELEMENT cercle    EMPTY >
     <!ELEMENT rectangle EMPTY>
     <!ELEMENT triangle  EMPTY>
     
     <!ATTLIST dessins largeur CDATA "REQUIRED">
     <!ATTLIST dessins hauteur CDATA "REQUIRED">
     
     <!ATTLIST cercle couleur %couleur;  "bleu" >
     <!ATTLIST cercle xcentre CDATA "REQUIRED">
     <!ATTLIST cercle ycentre CDATA "REQUIRED">
     <!ATTLIST cercle rayon   CDATA "REQUIRED">
     
     <!ATTLIST rectangle couleur %couleur; "bleu" >
     <!ATTLIST rectangle xcsg    CDATA "REQUIRED">
     <!ATTLIST rectangle ycsg    CDATA "REQUIRED">
     <!ATTLIST rectangle largeur CDATA "REQUIRED">
     <!ATTLIST rectangle hauteur CDATA "REQUIRED">
     
     <!ATTLIST triangle couleur %couleur; "bleu" >
     <!ATTLIST triangle xpoint1 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint1 CDATA "REQUIRED">
     <!ATTLIST triangle xpoint2 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint2 CDATA "REQUIRED">
     <!ATTLIST triangle xpoint3 CDATA "REQUIRED">
     <!ATTLIST triangle ypoint3 CDATA "REQUIRED">
     

On peut aller encore plus loin et intégrer la couleur par défaut dans l'entité ou décider qu'il est plus modulaire de définir une autre entité pour la couleur par défaut 


     <!-- une entité pour la liste des couleurs et la couleur par défaut   -->
     <!-- avec un exemple d'utilisation                                    -->
     
     <!ENTITY  % couleur "(bleu|jaune|rouge|vert) 'bleu' "                   >
     ...
     <!ATTLIST   cercle  couleur %couleur;                                   >
     
     <!-- une entité pour la liste des couleurs et une autre entité pour la couleur par défaut -->
     <!-- avec un exemple d'utilisation                                                        -->
     
     <!ENTITY % couleur     "(bleu|jaune|rouge|vert)"                                            >
     <!ENTITY % couleur_def "'bleu'"                                                             >
     ...
     <!ATTLIST  cercle      couleur %couleur;  %couleur_def;                                     >
     
     

Voici donc les 5 grammaires DTD possibles et leur traduction automatique en schéma XSD :

dessin1.dtd       dessin2.dtd       dessin3.dtd       dessin4.dtd       dessin5.dtd       

dessin1.xsd       dessin2.xsd       dessin3.xsd       dessin4.xsd       dessin5.xsd       

 

4. Validité suivant la grammaire

Ecrire un fichier minimal ouimaisnon.xml qui est valide pour la grammaire g1.dtd mais non valide pour la grammaire g2.dtd. Fournir ensuite un fichier livre.xml valide pour g2.dtd.

Solution :  

Il ne faut pas se laisser impressionner par la longueur de la grammaire g1.dtd car de nombreux de ses éléments sont facultatifs :


     <!-- g1.dtd  -->
     
     <!ELEMENT livre (preface?,introduction?,chapitres*,conclusion?,annexes?,references,infosPublications) >
     <!ELEMENT preface       (#PCDATA)           >
     <!ELEMENT introduction  (#PCDATA)           >
     <!ELEMENT chapitres     (#PCDATA|chapitre)* >
     <!ELEMENT chapitre      (section*)          >
     <!ELEMENT section       (#PCDATA)           >
     <!ELEMENT conclusion    (#PCDATA)           >
     <!ELEMENT annexes       (#PCDATA)           >
     <!ELEMENT autre         (#PCDATA)           >
     <!ELEMENT suite         (#PCDATA)           >
     <!ELEMENT references        ANY             >
     <!ELEMENT infosPublications EMPTY           >
     

Donc si on se focalise sur les éléments obligatoires, on peut se limiter à un fichier ouimaisnon.xml comme celui-ci :


     <?xml version="1.0" ?>
     <!-- ouimaisnon.xml -->
     
     <livre>
     <references>
     pomme
     <autre>
     poire
     </autre>
     scoubidou
     <suite />
     fin
     </references>
     <infosPublications />
     </livre>
     

On notera au passage ce que permet le mot clé ANY pour l'élément references, qui, s'il permet de tester des documents en cours de rédaction, n'est pas très contraignant. De même en ce qui concerne (#PCDATA|chapitre)* pour l'élément chapitres. Pour vérifier que ce fichier est valide pour g1 mais non pour g2, le plus simple est d'utiliser xmlstarlet ou xmllint car on n'a pas besoin d'écrire explicitement <!DOCTYPE avant l'élément racine, ce que ne permet pas rxp :


     $gh>  rxp -s -v ouimaisnon.xml # n'affiche rien, donc ceci prouve que le fichier est bien formé
     
     RXP 1.5.0 Copyright Richard Tobin, LTG, HCRC, University of Edinburgh
     Input encoding UTF-8, output encoding UTF-8
     
     $gh>  rxp -s -V ouimaisnon.xml
     
     Warning: Document has no DTD, validating abandoned
     (detected at end of prolog of document file:///home/gh/public_html/Webrd/ouimaisnon.xml)
     
     $gh>  xmlstarlet val -d g1.dtd ouimaisnon.xml
     
     ouimaisnon.xml - valid
     
     $gh>  xmlstarlet val -d g2.dtd ouimaisnon.xml
     
     ouimaisnon.xml - invalid
     
     $gh>  xmlstarlet val -e -d g2.dtd ouimaisnon.xml # il faut utiliser l'option -e pour voir le détail des erreurs
     
     ouimaisnon.xml:4.0: Element livre content does not follow the DTD,
     expecting (preface? , introduction? , partie+ , conclusion? , annexes? , references , infosPublications),
     got (references infosPublications )
     ouimaisnon.xml:5.0: Element references was declared EMPTY this one has content
     ouimaisnon.xml:5.0: Element references does not carry attribute motclef
     ouimaisnon.xml:5.0: Element references does not carry attribute txtref
     ouimaisnon.xml:7.0: No declaration for element autre
     ouimaisnon.xml:11.0: No declaration for element suite
     ouimaisnon.xml:14.0: Element infosPublications does not carry attribute nref
     ouimaisnon.xml - invalid
     
     $gh>  xmllint --noout --dtdvalid g1.dtd ouimaisnon.xml # n'affiche rien car valide pour g1
     
     $gh>  xmllint --noout --dtdvalid g2.dtd ouimaisnon.xml
     
     ouimaisnon.xml:4: element livre: validity error : Element livre content does not follow the DTD,
     expecting (preface? , introduction? , partie+ , conclusion? , annexes? , references , infosPublications),
     got (references infosPublications )
     ouimaisnon.xml:5: element references: validity error : Element references was declared EMPTY this one has content
     ouimaisnon.xml:5: element references: validity error : Element references does not carry attribute motclef
     ouimaisnon.xml:5: element references: validity error : Element references does not carry attribute txtref
     ouimaisnon.xml:7: element autre: validity error : No declaration for element autre
     ouimaisnon.xml:11: element suite: validity error : No declaration for element suite
     ouimaisnon.xml:14: element infosPublications: validity error : Element infosPublications does not carry attribute nref
     Document ouimaisnon.xml does not validate against g2.dtd
     
     $gh>  xmllint --noout --dtdvalid g2.dtd livre.xml # n'affiche rien car valide pour g2
     
     $gh>  xmlstarlet val -d g2.dtd livre.xml
     
     livre.xml - valid
     
     

Par contre, la grammaire g2.dtd est plus exigeante que la grammaire g1.dtd :


     <!-- g2.dtd  -->
     
     <!ELEMENT livre (preface?,introduction?,partie+,conclusion?,annexes?,references,infosPublications) >
     <!ELEMENT preface           (#PCDATA)          >
     <!ELEMENT introduction      (#PCDATA)          >
     <!ELEMENT partie            (chapitre+)        >
     <!ELEMENT chapitre          (#PCDATA|section)* >
     <!ELEMENT section           (#PCDATA)          >
     <!ELEMENT conclusion        (#PCDATA)          >
     <!ELEMENT annexes           (#PCDATA)          >
     <!ELEMENT references        EMPTY              >
     <!ELEMENT infosPublications EMPTY              >
     
     <!ATTLIST partie        numpart ID       #IMPLIED   >
     <!ATTLIST chapitre      numchap ID       #REQUIRED  >
     <!ATTLIST section       numsec  ID       "REQUIRED" >
     <!ATTLIST references    numref  ID       #IMPLIED   >
     <!ATTLIST references    txtref  CDATA    #REQUIRED  >
     <!ATTLIST references    motclef NMTOKEN  #REQUIRED  >
     <!ATTLIST infosPublications nref IDREF   #REQUIRED  >
     <!ATTLIST infosPublications date CDATA   "?"        >
     

Donc un fichier livre.xml valide pour g2 est un peu plus technique et demande un peu plus de réflexion :


     <?xml version="1.0" encoding="ISO-8859-15" ?>
     <!-- livre.xml -->
     
     <livre>
     
     <preface>
     Ceci est la préface.
     </preface>
     
     <introduction>
     Ceci est l'introduction.
     </introduction>
     
     <partie numpart="partie1">
     <chapitre numchap="chap1.1">
     Ceci est le chapitre 1 de la partie 1.
     </chapitre>
     <chapitre numchap="chap1.2">
     Ceci est le chapitre 2 de la partie 1.
     </chapitre>
     </partie>
     
     <partie>
     <!-- partie 2 mais sans numpart explicite -->
     <chapitre numchap="chap2.1">
     Ceci est le chapitre 1 de la partie 2.
     <section numsec="sec2.1.1">
     ceci est etc.
     </section>
     <section numsec="sec2.1.2">
     ceci est etc.
     </section>
     </chapitre>
     </partie>
     
     <partie numpart="partie3">
     <chapitre numchap="chap3.1">
     Ceci est le chapitre 1 de la partie 3.
     </chapitre>
     </partie>
     
     <conclusion>
     Ceci est la conclusion.
     </conclusion>
     
     <references numref="ref1" txtref="standard" motclef="demo" />
     
     <infosPublications nref="ref1" />
     
     </livre>
     

Afin de vous entrainer à la lecture synoptique et comparative de DTD et XSD, voici donc les 2 grammaires DTD possibes et leur traduction automatique en schéma XSD :

g1.dtd       g2.dtd       g1.xsd       g2.xsd       

 

5. Créer des grammaires DTD et XSD

Disons qu'un trajet ferroviaire est représenté schématiquement mais obligatoirement par un numéro identifiant de trajet, un type de train, un lieu de départ et un lieu d'arrivée. Ces informations sont éventuellement complétées, lorsqu'elles sont disponibles, par deux informations ts (pour time stamp en anglais) qui fournissent respectivement la date et heure de départ et la date et heure d'arrivée.

Voici un exemple de fichier XML regroupant deux trajets, nommé trajets.xml.

Fichier trajets.xml :


     <trajets>
       <trajet id="tr02436">
            <train>TGV </train>
            <depart>Angers</depart>
            <arrive>Paris Montparnasse</arrive>
       </trajet>
       <trajet id="tr5897">
            <train>Micheline </train>
            <depart ts="2016-09-08 13:00">Troyes</depart>
            <arrive ts="2016-09-08 17:08">Dijon</arrive>
       </trajet>
     </trajets>
     

Donner pour ce fichier trajets.xml une grammaire DTD minimale et raisonnable qui permet de décrire les trajets ferroviaires puis une grammaire XSD équivalente.

Solution :  

On lira attentivement les fichiers ci-dessous.

Grammaire trajets.dtd :


     <!ELEMENT trajets (trajet)+                 >
     <!ELEMENT trajet  (train,depart,arrive)     >
     <!ELEMENT train   (#PCDATA)                 >
     <!ELEMENT depart  (#PCDATA)                 >
     <!ELEMENT arrive  (#PCDATA)                 >
     
     <!ATTLIST trajet  xmlns CDATA #FIXED '' id NMTOKEN #REQUIRED  >
     <!ATTLIST depart  xmlns CDATA #FIXED '' ts CDATA #IMPLIED     >
     <!ATTLIST arrive  xmlns CDATA #FIXED '' ts CDATA #IMPLIED     >
     

Grammaire trajets.xsd :


     <?xml version="1.0" encoding="UTF-8"?>
     <xsd:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
     
       <xsd:element name="trajets">
         <xsd:complexType>
           <xsd:sequence>
             <xsd:element maxOccurs="unbounded" ref="trajet"/>
           </xsd:sequence>
         </xsd:complexType>
       </xsd:element>
     
       <xsd:element name="trajet">
         <xsd:complexType>
           <xsd:sequence>
             <xsd:element ref="train"/>
             <xsd:element ref="depart"/>
             <xsd:element ref="arrive"/>
           </xsd:sequence>
           <xsd:attribute name="id" use="required" type="xsd:NCName"/>
         </xsd:complexType>
       </xsd:element>
     
       <xsd:element name="train" type="xsd:NCName"/>
     
       <xsd:element name="depart">
         <xsd:complexType>
           <xsd:simpleContent>
             <xsd:extension base="xsd:NCName">
               <xsd:attribute name="ts"/>
             </xsd:extension>
           </xsd:simpleContent>
         </xsd:complexType>
       </xsd:element>
     
       <xsd:element name="arrive">
         <xsd:complexType mixed="true">
           <xsd:attribute name="ts"/>
         </xsd:complexType>
       </xsd:element>
     
     </xsd:schema>
     

Au passage, vous aurez remarqué que la grammaire proposée est invalide puisque l'espace de noms proposé (xs) ne correspond pas aux préfixes utilisés (xsd). Il faut donc juste modifier la ligne 1 pour qu'il y ait écrit xmlns:xsd= au lieu de xmlns:xs=.

 

6. Non unicité des grammaires DTD et XSD

Ecrire une grammaire pour le fichier films2 en DTD puis en XSD. Où y a-t-il des choix à faire ? Pourquoi ne peut-on pas parler de la grammaire DTD d'un fichier ?

Peut-on créer automatiquement des grammaires à partir d'exemples ? Que faut-il attendre de ce genre de production automatique ?

Solution :  

La grammaire pour films2.xml est un peu compliquée à écrire. D'abord parce qu'il y a une hiérarchie un peu moins évidente. Ensuite, parce que certains éléments ne sont pas renseignés, ce qui n'est pas très facile à détecter car les éléments absents ne le sont pas en début de fichier : certains films n'ont pas d'élément GENRE et certains films n'ont pas d'élément RESUME, certains artistes n'ont pas d'élément ANNEENAISS.

On pourrait éventuellement avoir une idée de la concomittance entre éléments et attributs à l'aide notre programme eltatt si on l'applique à films5.xml soit le résultat suivant.

On peut donc écrire la grammaire minimale suivante qui autorise à n'avoir aucun film ni aucun artiste :


     <!ELEMENT FILMSETARTISTES (FILMS,ARTISTES)>
     
     <!ELEMENT FILMS    (FILM)+>
     <!ELEMENT FILM     (TITRE,GENRE?,PAYS,MES,ROLES,RESUME?)  >
     <!ELEMENT TITRE    (#PCDATA)                              >
     <!ELEMENT GENRE    (#PCDATA)                              >
     <!ELEMENT PAYS     (#PCDATA)                              >
     <!ELEMENT MES                                      EMPTY  >
     <!ELEMENT ROLES    (ROLE)*                                >
     <!ELEMENT RESUME   (#PCDATA)                              >
     
     <!ELEMENT ROLE     (PRENOM,NOM,INTITULE) >
     <!ELEMENT PRENOM   (#PCDATA)             >
     <!ELEMENT INTITULE (#PCDATA)             >
     <!ELEMENT NOM      (#PCDATA)             >
     
     <!ELEMENT ARTISTES   (ARTISTE)+                      >
     <!ELEMENT ARTISTE    (ARTNOM,ARTPRENOM,ANNEENAISS?)  >
     <!ELEMENT ARTNOM     (#PCDATA)                       >
     <!ELEMENT ARTPRENOM  (#PCDATA)                       >
     <!ELEMENT ANNEENAISS (#PCDATA)                       >
     
     <!ATTLIST ARTISTE xmlns CDATA #FIXED ''   id    CDATA #REQUIRED >
     <!ATTLIST FILM    xmlns CDATA #FIXED ''   Annee CDATA #REQUIRED >
     <!ATTLIST MES     xmlns CDATA #FIXED ''   idref CDATA #REQUIRED >
     

Il n'est pas possible de profiter directement des déclarations ID et IDREF des DTD car dans la norme XML un identifiant ou une référence à un identifiant ne peut pas être uniquement numérique (de façon plus précise, il ne doit pas commencer par un chiffre). Or, c'est souvent le cas lorsque l'attribut provient d'un champ autoincrémenté d'une base de données. Voici ce qui se passe si on essaie de valider :


     @ghchu~/public_html/Webrd|(~gH) > xmlstarlet val -d films2.dtd films2dtd.xml
     films2dtd.xml - valid
     
     @ghchu~/public_html/Webrd|(~gH) > xmlstarlet val -d films3.dtd films2dtd.xml  | head
     films2dtd.xml:7: element MES: validity error : Syntax of value for attribute idref of MES is not valid
     films2dtd.xml:19: element MES: validity error : Syntax of value for attribute idref of MES is not valid
     films2dtd.xml:29: element MES: validity error : Syntax of value for attribute idref of MES is not valid
     films2dtd.xml:41: element MES: validity error : Syntax of value for attribute idref of MES is not valid
     ...
     films2dtd.xml:591: element ARTISTE: validity error : Syntax of value for attribute id of ARTISTE is not valid
     films2dtd.xml:592: element ARTISTE: validity error : Syntax of value for attribute id of ARTISTE is not valid
     films2dtd.xml:593: element ARTISTE: validity error : Syntax of value for attribute id of ARTISTE is not valid
     films2dtd.xml:594: element ARTISTE: validity error : Syntax of value for attribute id of ARTISTE is not valid
     films2dtd.xml:595: element ARTISTE: validity error : Syntax of value for attribute id of ARTISTE is not valid
     films2dtd.xml:596: element ARTISTE: validity error : Syntax of value for attribute id of ARTISTE is not valid
     ...
     
     @ghchu~/public_html/Webrd|(~gH) > xmlstarlet val -s films2.xsd films2.xml
     films2.xml - valid
     
     @ghchu~/public_html/Webrd|(~gH) > xmlstarlet val -s films3.xsd films2.xml
     films2.xml - invalid
     
     @ghchu~/public_html/Webrd|(~gH) > xmlstarlet val -e -s films3.xsd films2.xml
     films2.xml:6: element MES: Schemas validity error : Element 'MES', attribute 'idref': '3' is not a valid value of the atomic type 'xs:IDREF'.
     films2.xml:18: element MES: Schemas validity error : Element 'MES', attribute 'idref': '4' is not a valid value of the atomic type 'xs:IDREF'.
     films2.xml:28: element MES: Schemas validity error : Element 'MES', attribute 'idref': '6' is not a valid value of the atomic type 'xs:IDREF'.
     films2.xml:40: element MES: Schemas validity error : Element 'MES', attribute 'idref': '9' is not a valid value of the atomic type 'xs:IDREF'.
     films2.xml:46: element MES: Schemas validity error : Element 'MES', attribute 'idref': '10' is not a valid value of the atomic type 'xs:IDREF'.
     films2.xml:58: element MES: Schemas validity error : Element 'MES', attribute 'idref': '13' is not a valid value of the atomic type 'xs:IDREF'.
     ...
     films2.xml:590: element ARTISTE: Schemas validity error : Element 'ARTISTE', attribute 'id': '6' is not a valid value of the atomic type 'xs:ID'.
     films2.xml:590: element ARTNOM: Schemas validity error : Element 'ARTNOM': This element is not expected. Expected is ( ACTNOM ).
     films2.xml:591: element ARTISTE: Schemas validity error : Element 'ARTISTE', attribute 'id': '3' is not a valid value of the atomic type 'xs:ID'.
     films2.xml:591: element ARTNOM: Schemas validity error : Element 'ARTNOM': This element is not expected. Expected is ( ACTNOM ).
     ...
     
     
     

Si on modifie le fichier des films en ajoutant les 3 lettres art devant chaque identifiant et chaque référence d'identifiant, soit :


     # version avec des id et des idrefs numériques
     
     <?xml version="1.0" encoding="ISO-8859-1"?>
     <!-- films3dtd.xml -->
     <!DOCTYPE FILMSETARTISTES SYSTEM "films3.dtd">
     <FILMSETARTISTES>
     <FILMS>
     <FILM Annee='1958'>
     <TITRE>Vertigo</TITRE>
     <GENRE>Drame</GENRE><PAYS>USA</PAYS><MES idref="3"></MES>
     ...
     <ARTISTE id="119"><ACTNOM>Parillaud</ACTNOM><ACTPNOM>Anne</ACTPNOM><ANNEENAISS>1960</ANNEENAISS></ARTISTE>
     <ARTISTE id="120"><ACTNOM>Anglade</ACTNOM><ACTPNOM>Jean-Hughes</ACTPNOM><ANNEENAISS>1955</ANNEENAISS></ARTISTE>
     <ARTISTE id="121"><ACTNOM>Barr</ACTNOM><ACTPNOM>Jean-Marc</ACTPNOM><ANNEENAISS>1960</ANNEENAISS></ARTISTE>
     </ARTISTES>
     </FILMSETARTISTES>
     
     # version avec des id et des idrefs qui commencent par art
     # (seuls ces id et idrefs sont modélisables par ID et IDREF dans une DTD)
     
     <?xml version="1.0" encoding="ISO-8859-1"?>
     <!-- films3dtd.xml -->
     <!DOCTYPE FILMSETARTISTES SYSTEM "films3.dtd">
     <FILMSETARTISTES>
     <FILMS>
     <FILM Annee='1958'>
     <TITRE>Vertigo</TITRE>
     <GENRE>Drame</GENRE><PAYS>USA</PAYS><MES idref="art3"></MES>
     ...
     <ARTISTE id="art119"><ACTNOM>Parillaud</ACTNOM><ACTPNOM>Anne</ACTPNOM><ANNEENAISS>1960</ANNEENAISS></ARTISTE>
     <ARTISTE id="art120"><ACTNOM>Anglade</ACTNOM><ACTPNOM>Jean-Hughes</ACTPNOM><ANNEENAISS>1955</ANNEENAISS></ARTISTE>
     <ARTISTE id="art121"><ACTNOM>Barr</ACTNOM><ACTPNOM>Jean-Marc</ACTPNOM><ANNEENAISS>1960</ANNEENAISS></ARTISTE>
     </ARTISTES>
     </FILMSETARTISTES>
     
     

alors, on peut utiliser ID et IDREF comme suit :


     <!-- films3.dtd -->
     
     <!ELEMENT FILMSETARTISTES (FILMS?,ARTISTES?)              >
     
     <!ELEMENT FILMS    (FILM*)                                >
     <!ELEMENT FILM     (TITRE,GENRE?,PAYS,MES,ROLES,RESUME?)  >
     <!ELEMENT TITRE    (#PCDATA)                              >
     <!ELEMENT GENRE    (#PCDATA)                              >
     <!ELEMENT PAYS     (#PCDATA)                              >
     <!ELEMENT MES      (#PCDATA)                              >
     <!ELEMENT ROLES    (ROLE*)                                >
     <!ELEMENT ROLE     (PRENOM,NOM,INTITULE)                  >
     <!ELEMENT PRENOM   (#PCDATA)                              >
     <!ELEMENT NOM      (#PCDATA)                              >
     <!ELEMENT INTITULE (#PCDATA)                              >
     <!ELEMENT RESUME   (#PCDATA)                              >
     <!ATTLIST FILM     Annee   CDATA #REQUIRED                >
     <!ATTLIST MES      idref   IDREF #REQUIRED                >
     
     <!ELEMENT ARTISTES   (ARTISTE*)                           >
     <!ELEMENT ARTISTE    (ACTNOM,ACTPNOM,ANNEENAISS?)         >
     <!ELEMENT ACTNOM     (#PCDATA)                            >
     <!ELEMENT ACTPNOM    (#PCDATA)                            >
     <!ELEMENT ANNEENAISS (#PCDATA)                            >
     <!ATTLIST ARTISTE    id  ID #REQUIRED                     >
     

Voici par exemple les messages d'erreur si on oublie de fournir un identifiant, si on définit deux fois le même identifiant ou si on fait référence à un identifiant qui n'existe pas (nous avons mis art3333 au lieu de art3 comme référence pour le premier film) :


     $gh> rxp -s -V films4dtd.xml
     
     Warning: Required attribute idref for element MES is not present
     in unnamed entity at line 20 char 52 of file:///home/gh/public_html/Webrd/films4dtd.xml
     
     Warning: Duplicate ID attribute value art6
     in unnamed entity at line 593 char 19 of file:///home/gh/public_html/Webrd/films4dtd.xml
     
     Warning: The ID art3333 was referred to but never defined
     (detected at end of body of document file:///home/gh/public_html/Webrd/films4dtd.xml)
     
     
     $gh> xmllint --noout --dtdvalid films3.dtd --valid films4dtd.xml
     
     films4dtd.xml:20: element MES: validity error : Element MES does not carry attribute idref
     <GENRE>Science-fiction</GENRE><PAYS>USA</PAYS><MES></MES>
                                                              ^
     films4dtd.xml:593: element ARTISTE: validity error : ID art6 already defined
     <ARTISTE id="art6"><ACTNOM>Cameron</ACTNOM><ACTPNOM>James</ACTPNOM><ANNEENAISS>1
                       ^
     films4dtd.xml:712: element MES: validity error : IDREF attribute idref references an unknown ID "art3333"
     
     ^
     films4dtd.xml:20: element MES: validity error : Element MES does not carry attribute idref
     films4dtd.xml:593: element ARTISTE: validity error : ID art6 already defined
     films4dtd.xml:8: element MES: validity error : IDREF attribute idref references an unknown ID "art3333"
     Document films4dtd.xml does not validate against films3.dtd
     
     $gh> xmlstarlet val -d films3.dtd -e films4dtd.xml
     
     films4dtd.xml:593.12: ID art6 already defined
     <ARTISTE id="art6"><ACTNOM>Cameron</ACTNOM><ACTPNOM>James</ACTPNOM><ANNEENAISS>1
                       ^
     films4dtd.xml:20.0: Element MES does not carry attribute idref
     films4dtd.xml:593.0: ID art6 already defined
     films4dtd.xml:8.0: IDREF attribute idref references an unknown ID "art3333"
     films4dtd.xml - invalid
     
     
     

Nous verrons dans la prochaine série d'exercices, avec les transformations XSL, comment ajouter automatiquement les trois lettres art pour les attributs id et idfref, même si on peut le faire sous éditeur avec Edition/Remplacer tout ou avec un outil en ligne de commande comme sed ou perl en mode -i -e.

Il est clair qu'on ne peut pas parler de la grammaire d'un fichier XML, même si on précise DTD ou XSD car les exemples précécents montrent qu'on peut définir plusieurs grammaires pour lesquelles un même fichier XML est valide. C'est pourquoi on ne doit jamais dire qu'un fichier fichier XML est valide sans préciser la grammaire utilisée alors qu'il est tout-à-fait normal de dire qu'un fichier est bien formé sans rien préciser d'autre.

Voici les grammaires utilisées :

films2.dtd       films3.dtd       films2.xsd       films3.xsd       

Il existe des outils pour créer automatiquement des grammaires à partir d'exemples, comme par exemple trang dont le manuel est ici. Exemples d'utilisation :


     # génération de grammaire DTD à partir de fichier(s) XML via trang, forme longue
     
     java -jar $trangPath trang.jar -I xml -O dtd -i encoding=ISO8859-15 serv00.xml serv00.dtd
     
     # génération de grammaire XSD à partir de fichier(s) XML via trang, forme courte
     
     trang serv00.xml serv00.xsd
     

 

7. Vérification des vérificateurs

Pour vérifier un document XML, on utilise une grammaire DTD ou XSD. Mais comment vérifier une grammaire DTD ou XSD ?

On pourra utiliser les grammaires incorrectes bad.dtd et bad.xsd associées respectivement aux fichiers baddtd.xml et badxsd.xml pour vérifier votre vérification de vérificateurs (!).

Solution :  

Exercice volontairement corrigé. Quoique...Venez en T.P. !

 

8. Rajout de contraintes

Reprendre l'exercice 1 (la molécule) et définir un schéma XSD plus restrictif que la DTD avec les règles suivantes :

  • le nom de l'atome doit comporter au moins deux lettres (au fait, pourquoi ?)
  • le nombre d'atomes doit être un entier non négatif
  • l'attribut lettre de l'atome doit comporter au moins une lettre en majuscule, suivie au maximum d'une lettre en minuscule (pourquoi ?)

Solution :  

Exercice volontairement corrigé. Quoique...Venez en T.P. !

 

 

Code-source php de cette page.

 

 

retour gH    Retour à la page principale de   (gH)