TP 6, 04/11/2009

Par Benoît Valiron <benoit.valiron [at] monoidal.net> et Jean Baptiste FADDOUL <jean-baptiste.faddoul [at] xrce.xerox.com>

  1. Dans l'exercice 1 du TD 6 de hier, vous avez produit le schéma Relax-NG suivant.

    default namespace = "bio:court"
    namespace h = "http://www.w3.org/1999/xhtml"
    namespace dc = "http://purl.org/dc/1.1/elements/"
    
    dc:title [ "Biographie" ]
    dc:description [ "Ceci est un format pour une courte biographie" ]
    
    start = (
      [ dc:description [ "Racine de la biographie" ] ]
      element bio {
         [ dc:description [ "Nom de la personne" ] ]
         element nom { text },
         para
      }
    )
    
    para = (
      text | 
      element h:i { para } | 
      element h:b { para }
    )*

    Utilisez l'outil trang pour produire un schéma Relax-NG en syntaxe XML. Est-ce bien ce que nous avons fait hier ?

    Écrivez une courte biographie suivant ce schéma pour Charles M. Schulz contenant le texte :

    Charles M. Schulz (Minneapolis, États-Unis, 1922 - 12 février 2000) était scénariste et dessinateur de comic strips. C'est le père de Snoopy.

    Validez votre biographie.

    EXPLICATION ET CORRECTION

    Pour le validateur jing, la feuille de validation ne contient que

    default namespace = "bio:court"
    namespace h = "http://www.w3.org/1999/xhtml"
    namespace dc = "http://purl.org/dc/1.1/elements/"
    
    start = (
      element bio {
         element nom { text },
         para
      }
    )
    
    para = (
      text | 
      element h:i { para } | 
      element h:b { para }
    )*

    sans les éléments qui ne sont pas dans l'espace de nom de Dublin Core (langage non compris par jing).

    Ce schéma simplifié correspond au schéma en syntaxe XML

    <?xml version="1.0" encoding="UTF-8"?>
    <grammar ns="bio:court" 
             xmlns:h="http://www.w3.org/1999/xhtml" 
             xmlns="http://relaxng.org/ns/structure/1.0">
      <start>
        <element name="bio">
          <element name="nom">
            <text/>
          </element>
          <ref name="para"/>
        </element>
      </start>
      <define name="para">
        <zeroOrMore>
          <choice>
            <text/>
            <element name="h:i">
              <ref name="para"/>
            </element>
            <element name="h:b">
              <ref name="para"/>
            </element>
          </choice>
        </zeroOrMore>
      </define>
    </grammar>

    Je vous invite à noter l'attribut ns pour désigner l'espace de nom par défaut des éléments que l'on définit et l'attribut xmlns pour désigner l'espace de nom par défaut des élément du schéma XML, à savoir le langage Relax-NG.

    On a un format de donnée très simple dont un exemple de document XML sans espace de nom correspondrait à

    <bio>
     <nom>...</nom>
     ... ... <i> ... </i> ... <b> ... </b> ...
     ... <b> ... </b> ...
    </bio>

    Ce document n'est pas valable : la feuille de validation Relax NG donne en plus des indications de type : bio et nom appartenant à l'espace de nom bio:court et i et b à l'espace de nom http://www.w3.org/1999/xhtml. Si on complète le modèle de document, on a

    <bio xmlns="bio:court" xmlns:html="http://www.w3.org/1999/xhtml">
     <nom>...</nom>
     ... ... <html:i> ... </html:i> ... <html:b> ... </html:b> ...
     ... <html:b> ... </html:b> ...
    </bio>

    Le document demandé est donc écrit comme suit :

    <bio xmlns="bio:court" xmlns:html="http://www.w3.org/1999/xhtml">
     <nom>Charles M. Schulz</nom>
     <html:b>Charles M. Schulz</html:b> (<html:i>Minneapolis,
     États-Unis</html:i>, 1922 - 12 février 2000) était
     <html:i><html:b>scénariste et dessinateur</html:b></html:i>
     de comic strips. C'est le père de <html:b>Snoopy</html:b>.
    </bio>

    Si vous voulez mettre un préfixe à tous les éléments, vous pouvez aussi écrire le document comme suit.

    <b:bio xmlns:b="bio:court" xmlns:html="http://www.w3.org/1999/xhtml">
     <b:nom>Charles M. Schulz</b:nom>
     <html:b>Charles M. Schulz</html:b> (<html:i>Minneapolis,
     États-Unis</html:i>, 1922 - 12 février 2000) était 
     <html:i><html:b>scénariste et dessinateur</html:b></html:i>
     de comic strips. C'est le père de <html:b>Snoopy</html:b>.
    </b:bio>

    Je vous invite à tester ces exemples avec jing.

  2. Dans cet exercice, on continue le TD 6 en faisant l'exercice 2 sur les sitemaps.

    Le moteur de recherche google propose un service aux webmasters pour l'indexation : les sitemaps. Il s'agit d'un format XML pour donner des informations succintes au robot d'indexation sur les pages qu'il peut rencontrer. Le format est le suivant (pris sur wikipedia) :

    • Élément <urlset>. Obligatoire. Racine du document.

    • Élément <url>. Obligatoire. Élément parent pour chaque entrée. Les éléments restant sont tous fils de cet élément.

    • Élément <loc>. Obligatoire. Contient l'adresse internet d'une page, incluant le protocole (http:// ou https://). Doit faire au maximum 2048 caractères de long.

    • Élément <lastmod>. Facultatif. La date de dernière modification du fichier, en format ISO ou plus simplement YYYY-MM-DD.

    • Élément <changefreq>. Facultatif. Fréquence à laquelle la page est modifiée en général : always,le hourly, daily, weekly, monthly, yearly, never.

    • Élément <priority>. Facultatif. L'importance relative de cette page par rapport aux autres. Valeur entre 0.0 et 1.0, valeur par défaut de 0.5.

    Est-il possible d'écrire une DTD qui prenne en compte toutes les contraintes ?

    CORRECTION ET COMMENTAIRE

    La réponse courte est non.

    En effet, avec une DTD la seule chose que l'on puisse imposer à un élément est de contenir du texte. On ne peut pas imposer un contenu numérique, encore moins avec des bornes. On ne peut pas non plus imposer un nombre limite de caractères.

    La meilleure DTD possible est

    <!ELEMENT urlset (url*)>
    <!ELEMENT url (loc,lastmod?,changefreq?,priority?)>
    <!ELEMENT loc (#PCDATA)>
    <!ELEMENT lastmod (#PCDATA)>
    <!ELEMENT changefreq (#PCDATA)>
    <!ELEMENT priority (#PCDATA)>

    Avec cette DTD, l'ordre des éléments dans url est imposé. Pour autoriser un ordre quelconque (cela ne vous serait pas demandé), il faudrait lister toutes les possibilités.

    Écrivez une feuille de validation Relax-NG qui réponde aussi précisemment que possible à la spécification : En particulier, utilisez la librairie de type de XML-Schema.

    On va écrire la feuille de validation au fur et à mesure.

    D'abord, on écrit juste les contraintes de structure, sans prendre en compte les types.

    element urlset {
       element url {
          element loc { text } &
          element lastmod { text }? &
          element changefreq { text }? &
          element priority { text }?
       }*
    }

    On place des esperluètes & pour autoriser un ordre quelconque.

    Ensuite, on a la contrainte sur loc. À la place de text, on va choisir le type anyURI de la bibliothèque de type XML-Schema. En syntaxe compacte, il suffit de lui adjoindre le préfixe xsd, comme suit.

    element urlset {
       element url {
          element loc { xsd:anyURI } &
          element lastmod { text }? &
          element changefreq { text }? &
          element priority { text }?
       }*
    }

    L'élément lastmod contient une date. Il est donc habile de choisir le type date de la bibliothèque de type :

    element urlset {
       element url {
          element loc { xsd:anyURI } &
          element lastmod { xsd:date }? &
          element changefreq { text }? &
          element priority { text }?
       }*
    }

    L'élément priority contient un nombre à virgule. Le type decimal va faire l'affaire :

    element urlset {
       element url {
          element loc { xsd:anyURI } &
          element lastmod { xsd:date }? &
          element changefreq { text }? &
          element priority { xsd:decimal }?
       }*
    }

    L'élément changefreq contient un choix parmi une liste de valeurs fixées. La syntaxe est la suivante :

    element urlset {
       element url {
          element loc { xsd:anyURI } &
          element lastmod { xsd:date }? &
          element changefreq { 
             "always" | "hourly" | "daily" | "weekly" |
             "monthly" | "yearly" | "never" 
          }? &
          element priority { xsd:decimal }?
       }*
    }

    Il reste les contraintes de taille et de valeurs maximales et minimales. On va utiliser les facettes. La contrainte de taille va être exprimé avec la facette maxLength :

    element urlset {
       element url {
          element loc { 
            xsd:anyURI {
                maxLength = "2048"
             }
          } &
          element lastmod { xsd:date }? &
          element changefreq { 
             "always" | "hourly" | "daily" | "weekly" |
             "monthly" | "yearly" | "never" 
          }? &
          element priority { xsd:decimal }?
       }*
    }

    La contrainte de valeurs limites par les facettes minInclusive et maxInclusive :

    element urlset {
       element url {
          element loc { 
            xsd:anyURI {
                maxLength = "2048"
             }
          } &
          element lastmod { xsd:date }? &
          element changefreq { 
             "always" | "hourly" | "daily" | "weekly" |
             "monthly" | "yearly" | "never" 
          }? &
          element priority {
             xsd:decimal {
                maxInclusive = "1.0" 
                minInclusive = "0.0"
             }
          }?
       }*
    }

    Il n'est pas possible de donner une valeur par défaut en Relax NG. L'idée sous-jacente est que la valeur par défaut est une convention entre celui qui écrit et celui qui lit. Il n'y a rien à vérifier dans le document de particulier, et un schéma de validation Relax NG ne fournit que des contraintes à vérifier.

    Le document Relax NG XML correspondant est le suivant :

    <?xml version="1.0" encoding="UTF-8"?>
    <element name="urlset" 
             xmlns="http://relaxng.org/ns/structure/1.0"
             datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
      <zeroOrMore>
        <element name="url">
          <interleave>
            <element name="loc">
              <data type="anyURI">
                <param name="maxLength">2048</param>
              </data>
            </element>
            <optional>
              <element name="lastmod">
                <data type="date"/>
              </element>
            </optional>
            <optional>
              <element name="changefreq">
                <choice>
                  <value>always</value>
                  <value>hourly</value>
                  <value>daily</value>
                  <value>weekly</value>
                  <value>monthly</value>
                  <value>yearly</value>
                  <value>never</value>
                </choice>
              </element>
            </optional>
            <optional>
              <element name="priority">
                <data type="decimal">
                  <param name="maxInclusive">1.0</param>
                  <param name="minInclusive">0.0</param>
                </data>
              </element>
            </optional>
          </interleave>
        </element>
      </zeroOrMore>
    </element>

    Notez l'apparition de l'attribut datatypeLibrary

  3. Exercice 3 du TD 6.

    On reprend ici le flux RSS de thèses en ligne. Un format DTD (très) simplifié est le suivant :

    <!ELEMENT rss (channel)>
    <!ATTLIST rss xmlns:dc CDATA #FIXED 
                              "http://purl.org/dc/elements/1.1/"
                  version CDATA #REQUIRED>
    <!ELEMENT channel (title,link,description,item*)>
    <!ELEMENT title (#PCDATA)>
    <!ELEMENT link (#PCDATA)>
    <!ELEMENT description (#PCDATA)>
    <!ELEMENT item (link|pubDate|title|dc:creator|dc:description|
                    dc:title|description)*>
    <!ELEMENT dc:creator (#PCDATA)>
    <!ELEMENT dc:description (#PCDATA)>
    <!ELEMENT dc:title (#PCDATA)>
    <!ELEMENT pubDate (#PCDATA)>

    Écrivez un schéma Relax-NG en syntaxe simplifiée et en syntaxe XML qui corresponde à cette DTD. (Attention aux espaces de noms!). Écrivez dans le format qui vous convient le mieux puis traduisez avec trang. Cela fait-il ce que vous attendez ?

    CORRECTION

    Écrivons d'abord le schéma en syntaxe compacte, c'est plus rapide.

    Tout d'abord, on a un espace de nom défini :

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        ...

    Deuxième étape, la racine est apparemment rss :

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        element rss {
           ...
        }

    Il y a un attribut et un seul fils :

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        element rss {
           attribute version { text },
           element channel {
              ...
           }
        }

    Ce fils contient une liste ordonnée de 4 fils dont le dernier peut apparaitre autant de fois que l'on souhaite :

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        element rss {
           attribute version { text },
           element channel {
              element title { ... },
              element link { ... },
              element description { ... },
              element item { ... }*
           }
        }

    Les trois premier contiennent du texte :

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        element rss {
           attribute version { text },
           element channel {
              element title { text },
              element link { text },
              element description { text },
              element item { ... }*
           }
        }

    L'élément item comporte au choix une liste d'éléments qui eux-même contiennent du texte, autant de fois que l'on veut. Certains de ces éléments ont le préfixe dc, correspondant à l'espace de nom Dublin Core. Le schéma au complet est donc :

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        element rss {
           attribute version { text },
           element channel {
              element title { text },
              element link { text },
              element description { text },
              element item {
               (
                 element link { text } |
                 element pubDate { text } |
                 element title { text } |
                 element dc:creator { text } |
                 element dc:description { text } |
                 element dc:title { text } |
                 element description { text }
               )*
              }*
           }
        }

    La version XML est la suivante :

    <?xml version="1.0" encoding="UTF-8"?>
    <element name="rss"
             xmlns:dc="http://purl.org/dc/elements/1.1/"
             xmlns="http://relaxng.org/ns/structure/1.0">
      <attribute name="version"/>
      <element name="channel">
        <element name="title">
          <text/>
        </element>
        <element name="link">
          <text/>
        </element>
        <element name="description">
          <text/>
        </element>
        <zeroOrMore>
          <element name="item">
            <zeroOrMore>
              <choice>
                <element name="link">
                  <text/>
                </element>
                <element name="pubDate">
                  <text/>
                </element>
                <element name="title">
                  <text/>
                </element>
                <element name="dc:creator">
                  <text/>
                </element>
                <element name="dc:description">
                  <text/>
                </element>
                <element name="dc:title">
                  <text/>
                </element>
                <element name="description">
                  <text/>
                </element>
              </choice>
            </zeroOrMore>
          </element>
        </zeroOrMore>
      </element>
    </element>

    Modifiez les schémas pour forcer l'élément pubDate à avoir comme type date, l'élément link le type anyURI et l'attribut version le type décimal et comme valeur soit 1.0, soit 2.0.

    CORRECTION

    Dans un premier temps, on change les types de pubDate et link.

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        element rss {
           attribute version { text },
           element channel {
              element title { text },
              element link { xsd:anyURI },
              element description { text },
              element item {
               (
                 element link { xsd:anyURI } |
                 element pubDate { xsd:date } |
                 element title { text } |
                 element dc:creator { text } |
                 element dc:description { text } |
                 element dc:title { text } |
                 element description { text }
               )*
              }*
           }
        }

    Maintenant, au tour de l'attribut version :

    namespace dc = "http://purl.org/dc/elements/1.1/"
        
        element rss {
           attribute version {
              xsd:decimal "1.0" | xsd:decimal "2.0" 
           },
           element channel {
              element title { text },
              element link { xsd:anyURI },
              element description { text },
              element item {
               (
                 element link { xsd:anyURI } |
                 element pubDate { xsd:date } |
                 element title { text } |
                 element dc:creator { text } |
                 element dc:description { text } |
                 element dc:title { text } |
                 element description { text }
               )*
              }*
           }
        }

    La version XML de ce schéma est

    <?xml version="1.0" encoding="UTF-8"?>
    <element name="rss" 
             xmlns:dc="http://purl.org/dc/elements/1.1/"
             xmlns="http://relaxng.org/ns/structure/1.0"
             datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
      <attribute name="version">
        <choice>
          <value type="decimal">1.0</value>
          <value type="decimal">2.0</value>
        </choice>
      </attribute>
      <element name="channel">
        <element name="title">
          <text/>
        </element>
        <element name="link">
          <data type="anyURI"/>
        </element>
        <element name="description">
          <text/>
        </element>
        <zeroOrMore>
          <element name="item">
            <zeroOrMore>
              <choice>
                <element name="link">
                  <data type="anyURI"/>
                </element>
                <element name="pubDate">
                  <data type="date"/>
                </element>
                <element name="title">
                  <text/>
                </element>
                <element name="dc:creator">
                  <text/>
                </element>
                <element name="dc:description">
                  <text/>
                </element>
                <element name="dc:title">
                  <text/>
                </element>
                <element name="description">
                  <text/>
                </element>
              </choice>
            </zeroOrMore>
          </element>
        </zeroOrMore>
      </element>
    </element>

    Notez l'apparition de l'attribut datatypeLibrary