TD XML, XSLT

1

 
 


COURS

 
Nous allons d’abord travailler sur  le fichier coursXML.xml qui est :

    6

 

3

 

2

 

ENSEIGNANTS

 

SUJET

 
<?xml version="1.0" encoding="ISO-8859-1"?>

<COURS CODE="TC234">

PROGRAMME

 
     <SUJET>Publication XSLT</SUJET>

     <ENSEIGNANTS>

          <!-- Enseignant responsable -->

 8

 

     7

 

5

 

4

 

Publication XSLT

 
          <NOM>Amann</NOM>

SEANCE

 

SEANCE

 

NOM

 

NOM

 
          <NOM>Rigaux</NOM>

     </ENSEIGNANTS>

     <PROGRAMME>

Rigaux

 

Amann

 
          <SEANCE ID="1">Documents XML</SEANCE>

ID=2

 

ID=1

 
          <SEANCE ID="2">Programmation XSLT</SEANCE>

          <ANNEE>2002</ANNEE>

     </PROGRAMME>

</COURS>

 

q       Un fichier XSLT qui affiche le contenu du document

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

   <xsl:template match="/">

        <xsl:value-of  select="/"/>

   </xsl:template>

</xsl:stylesheet>

 

La première partie de la règle sélectionne un nœud et la deuxième affiche le contenu du nœud.

 

q       Un fichier XSLT qui récupère les noms des enseignants :

q       Première tentative :

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">

      <xsl:value-of select="COURS/ENSEIGNANTS/NOM"/>

</xsl:template>

</xsl:stylesheet>

 

A l’exécution, ce fichier ne retourne qu’un seul nom. Essayons de comprendre pourquoi. Ce fichier contient une règle qui est déclenchée dès qu’on trouve un nœud associé au template. La règle est donc déclenchée pour un seul nœud (donc déclenchée une seule fois).

L’action de la règle est elle aussi déclenchée une seule fois. L’exécution de l’action consiste à chercher un nœud à partir du nœud contexte (racine) un nœud qui satisfait l’expression de chemin (s’y trouvant au bout) COURS/ENSEIGNANT/NOM et en récupérer la valeur associée. Comme l’action n’est exécutée qu’une seule fois, seule la valeur du premier nœud trouvé sera retournée (ça sera Amann)

 

2.       Deuxième tentative :

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/COURS">

      <xsl:value-of select="ENSEIGNANTS/NOM"/>

</xsl:template>

</xsl:stylesheet>

 

Pour les mêmes raisons que précédemment, la règle n’est déclenchée qu’une seule fois donc un seule nom sera retournée.

 

3.       Troisième tentative :

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

 

< !-- règle 1 -->

<xsl:template match="/">

      <xsl:apply-templates  select="//NOM"/>

</xsl:template>

 

<!--  règle 2 -->

<xsl:template match="NOM">

      <xsl:value-of select="."/>

</xsl:template>

</xsl:stylesheet>

 

Dans ce fichier, nous avons deux règles. La première sélectionne un nœud : le nœud racine (1). Son action consiste à appliquer (apply-templates) à chaque nœud du type NOM (4 et 5). Le système va donc chercher les templates dans le texte dont la partie match satisfait les nœuds sélectionnés.  Le système prend le premier nœud (i.e 4) et vérifie s’il est du type NOM (condition de la règle 2). C’est le cas, donc la règle est déclenchée et son action est exécutée. Le même raisonnement tient pour le nœud 5.

 

Remarques :

§       Dans la deuxième règle, si dans la partie match, on avait ENSEIGNANTS/NOM le résulat aurait été le même.

§       Dans la deuxième règle, si on avait mis dans la partie match juste le caractère * (qui veut dire, n’importe quoi, le résultat aurait été aussi le même.

 

q       Dupliquer la règle 2 et vérifier le résultat. Les noms ne sont pas affichés deux fois. La raison est que les processeurs XSLT, pour un nœud donné, appliquent une seule règle parmi celles qu’ils trouvent applicables. Il est possible de définir des priorités entre les règles, mais ceci est une autre histoire …

q       Tester quand même ces deux dernières alternatives après avoir ajouté des éléments NOM aux séances.

 

q   Maintenant, on veut afficher le nom du deuxième auteur.

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<!-- règle 1-->

<xsl:template match="/">

      <xsl:apply-templates  select="//NOM[position()=2]"/>

</xsl:template>

<!-- règle2-->

<xsl:template match="NOM">

      <xsl:value-of select="."/>

</xsl:template>

</xsl:stylesheet>

 

 

q       Générer un fichier XML qui est de la forme <Noms><Nom>Amann</Nom></Nom>Rigaux</Nom></Noms>.

 

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    

     <!-- règle 1-->

     <xsl:template match="/">

     <NOMS>

          <xsl:apply-templates  select="//NOM"/>

          </NOMS>

     </xsl:template>

 

     <!-- règle2-->

     <xsl:template match="NOM">

     <NOM>

          <xsl:value-of select="."/>

     </NOM>

     </xsl:template>

 

</xsl:stylesheet>

 

q       Compter le nombre de fils que le nœud ENSEIGNANTS a.

 

     <xsl:template match="/">

          <xsl:apply-templates select="COURS/ENSEIGNANTS"/>

     </xsl:template>

    

     <xsl:template match="ENSEIGNANTS">

          <xsl:value-of select="count(./*)"/>

     </xsl:template>

 

q       Toujours avec l’exemple précédent. On veut maintenant parcourir toutes les séances de cours, afficher pour chacune son numéro, le nombre total de séances et l’intitulé de la séance.

<?xml version="1.0" encoding="ISO-8859-1"?>

 

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" encoding="ISO-8859-1"/>

 

  <xsl:template match="/">

   <PROGRAMME>

    <xsl:apply-templates select="//PROGRAMME"/>     ßOn sélectionne un nœud PROGRAMME

   </PROGRAMME>

  </xsl:template>

 

  <xsl:template match="PROGRAMME">

          Programme:   

          <SEANCES>

                     <xsl:for-each select="SEANCE">                                              ß pour chaque séance trouvée (à partir du nœud PROGRAMME)

                         Séance <xsl:value-of  select="concat(position(), '/', last())"/> : ß le nœud courant ici est un nœud SEANCE

                         <xsl:value-of select="."/>

                    </xsl:for-each>     

          </SEANCES>

 

               Valable pour l'année <xsl:value-of select="ANNEE"/> ß le nœud courant ici est le nœud PROGRAMME

  </xsl:template>

 

</xsl:stylesheet>

 

Question : Est-ce qu’on  aurait pu se passer de la boucle for-each en faisant des appels récursifs (combinaisons de template et apply-templates) ?

 

q        XSLT permet l’utilisation de variables. Les différentes manières de les déclarer sont illustrées par les exemples suivants

<xsl :variable name=’v1’ select=’12’/>   ß définit une variable v1 et initialise sa valeur à 12

<xsl :variable name=’v2’ select=’/COURS/ENSEIGNANTS’/> ß définit une variable v2. Sa valeur est l’ensemble des nœuds de type ENSEIGNANTS

<xsl :variable name=’v3’> Ceci est <mot-cle>le contenu </mot-cle> de v3 </xsl :variable>

 

Pour chaque film, on veut afficher son titre et un message disant qu’il est récent ou ancien selon qu’il soit sorti avant ou après 1970 (fichiers VarGlobal.xsl et ListeFilms.xml)

 

<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0"

           xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  <xsl:output method="xml"  encoding="ISO-8859-1"/>

 

  <xsl:variable name="annee" select="1970"/> ß déclaration de la variable.

 

  <xsl:template match="FILMS">

          <xsl:apply-templates select="FILM"/>

   </xsl:template>

   <xsl:template match="FILM">

     <xsl:choose>    ß c’est l’équivalent du case of des langages de programmation

 

               <xsl:when test="ANNEE &lt; $annee"> ß on teste si la valeur courante du nœud ANNEE est inférieur à la valeur de la variable

                     "<xsl:value-of select="TITRE"/>" est ancien

                </xsl:when>

                <xsl:when test="ANNEE &gt;= $annee">

                     "<xsl:value-of select="TITRE"/>" est récent

                </xsl:when>

                <xsl:otherwise>  ß si aucune des deux conditions précédentes n’est vérifiée

                     De quand date "<xsl:value-of select="TITRE"/>" ?

               </xsl:otherwise>

      </xsl:choose>

   </xsl:template>

 

</xsl:stylesheet>

o      En vous inspirant du document précédent, écrire un fichier XSLT qui permet d’afficher le titre de chacun des films ainsi qu’un message disant s’il est plus ancien, plus récent ou contemporain du film Alien. Puis affiche le nombre de films plus anciens et plus récents que ce film.

 

q        Soit le fichier Notes.xml . On veut afficher la moyenne des notes de chaque film dans un tableau HTML.

<?xml version="1.0" encoding="ISO-8859-1"?>

<FILMS>

     <FILM>

          <TITRE>Vertigo</TITRE>

          <NOTES>

               <NOTE>3</NOTE>

               <NOTE>3</NOTE>

               <NOTE>3</NOTE>

               <NOTE>5</NOTE>

          </NOTES>

     </FILM>

     <FILM>

          <TITRE>Alien</TITRE>

          <NOTES>

               <NOTE>4</NOTE>

               <NOTE>3</NOTE>

               <NOTE>5</NOTE>

          </NOTES>

     </FILM>

     <FILM>

          <TITRE>Titanic</TITRE>

          <NOTES>

               <NOTE>2</NOTE>

               <NOTE>2</NOTE>

               <NOTE>4</NOTE>

               <NOTE>1</NOTE>

          </NOTES>

     </FILM>

     <FILM>

          <TITRE>Sleepy Hollow</TITRE>

          <NOTES>

               <NOTE>2</NOTE>

               <NOTE>4</NOTE>

               <NOTE>4</NOTE>

          </NOTES>

     </FILM>

     <FILM>

          <TITRE>American Beauty</TITRE>

          <NOTES>

               <NOTE>5</NOTE>

               <NOTE>4</NOTE>

               <NOTE>4</NOTE>

          </NOTES>

     </FILM>

     <FILM>

          <TITRE>Impitoyable</TITRE>

          <NOTES>

               <NOTE>2</NOTE>

               <NOTE>1</NOTE>

               <NOTE>5</NOTE>

          </NOTES>

     </FILM>

</FILMS>

 

 

L’idée consiste donc à parcourir la liste des notes de chaque film, en faire la somme puis diviser cette somme par le nombre de notes.

 

Le pseudo-code suivant décrit la fonction :

Fonction somme (liste l)

Début

            Si (l n’est pas vide) alors

                        Retourner l[1] + somme(l[2..n])

            Sinon   

Retourner 0

Fin

 

Voici la portion XLT qui permet de la simuler

 

<xsl:template name="CalculTotal">  ß nom de la règle

     <xsl:param name="listeNotes"/>    ß déclaration d’un paramètre

 

      <xsl:choose>

     <xsl:when test="$listeNotes">    ß on teste si la liste des notes n’est pas vide

          <xsl:variable name="note" select="$listeNotes[1]"/> ß on affecte la première note

 

          <xsl:variable name="autresNotes">  ß on déclare une variable

               <xsl:call-template name="CalculTotal"> ß appel de la règle

                    <xsl:with-param name="listeNotes" select="$listeNotes[position() != 1]"/>

                     ß on sélectionne toutes les notes sauf la 1ère

               </xsl:call-template>

          </xsl:variable>

 

          <xsl:value-of select="$note + $autresNotes"/>

     </xsl:when>

     <xsl:otherwise>0</xsl:otherwise>

    </xsl:choose>

</xsl:template>

 

l’appel à la fonction se fait à l’intérieur d’une règle. On définit d’abord la première règle qui construit la table et qui sélectionne les nœuds FILM

 

<xsl:template match="FILMS">

<html>

     <head><title>Statistiques</title></head>

      <body bgcolor="white">

               <center>

                    <h1>Moyenne des notes pour chaque film</h1>

               <table border='1'>

                    <xsl:apply-templates select="FILM"/>  ß sélection des nœuds FILM

               </table>

               </center>

      </body>

 </html>

 </xsl:template>

 

Ensuite on définit une 2ème règle qui utilise chacun de ces nœuds pour calculer la moyenne des notes

<xsl:template match="FILM">

     <xsl:variable name="total">

               <xsl:call-template name="CalculTotal"> ß appel à la règle « CalculTotal »

               <xsl:with-param name="listeNotes"  select="NOTES/NOTE"/>  ß la liste des notes est affectée à listeNotes

          </xsl:call-template>

      </xsl:variable>

      <tr>

               <td><b><xsl:value-of select="TITRE"/></b></td>

          <td align="right">

               <xsl:value-of select="$total div count(NOTES/NOTE)"/>

         </td>

      </tr>

  </xsl:template>

 

Après la définition de ces deux règles, on reprend la 3ème règle qui fait le calcul de la fonction. Vous pouvez reprendre le fichier BoucleNotes.xsl

 

q        Considérons maintenant le fichier suivant (FilmsGroup.xml):

<?xml version="1.0" encoding="ISO-8859-1"?>

<FILMS>

     <FILM Annee="1997">

          <TITRE>Titanic</TITRE>

          <GENRE>Drame</GENRE>

          <PAYS>USA</PAYS>

     </FILM>

     <FILM Annee="1999">

          <TITRE>Sleepy Hollow</TITRE>

          <GENRE>Fantastique</GENRE>

          <PAYS>USA</PAYS>

     </FILM>

     <FILM Annee="1999">

          <TITRE>American Beauty</TITRE>

          <GENRE>Comédie</GENRE>

          <PAYS>USA</PAYS>

     </FILM>

     <FILM Annee="1992">

          <TITRE>Impitoyable</TITRE>

          <GENRE>Western</GENRE>

          <PAYS>USA</PAYS>

     </FILM>

     <FILM Annee="1997">

          <TITRE>Volte/Face</TITRE>

          <GENRE>Action</GENRE>

          <PAYS>USA</PAYS>

     </FILM>

     <FILM Annee="1995">

          <TITRE>Seven</TITRE>

          <GENRE>Policier</GENRE>

          <PAYS>USA</PAYS>

     </FILM>

     <FILM Annee="1995">

          <TITRE>L'armée des douze singes</TITRE>

          <GENRE>Science-fiction</GENRE>

          <PAYS>USA</PAYS>

     </FILM>

</FILMS>

 

 

On veut afficher pour chaque année, l’ensemble des titres de films. Il s’agit donc de faire un regroupement a la SQL (GROUP BY annee). La difficulté ici réside dans le fait qu’il existe autant d’années que de films. Certaines se répetent. Comment faire de sorte à n’extraire qu’un seul exemplaire ?

 

Ayant résolu ce problème, et en supposant que l’année courante soit stockée dans une variable $anneeCourante, il est facile d’obtenir les titres et genres de film avec

 

<ol>

     <xsl:for-each select="//FILM[@Annee=$anneeCourante]">

          <li>     

               <xsl:value-of select="concat(TITRE, ', ',  GENRE)"/>

               </li>

        </xsl:for-each>

 </ol>

 

 

Le principe de l’élimination des doublons d’une liste consiste à trier cette liste ensuite ne garder un élément que s’il est différent de son prédécesseur

 

Fonction eliminationDoublon(Liste l)

Début

            Trier l

            Garder l[1]

            Pour i=2 à longueur(l) Faire

                        Si l[i] est différent de l[i-1] Alors

                                   Garder l[i]

                        FinSi

            FinPour

Fin

 

L’idée consiste donc à récupérer la liste des films triée sur l’année. Pour chacun, n’afficher son année que si c’est la première fois qu’on la rencontre. Ceci peut se faire par :

 

<xsl:for-each select="FILM">

        <xsl:sort select="@Annee"/>  ß Cette structure permet de parcourir les FILM selon

                                                  l’ordre croissant de l’année

A chaque itération, on récupère l’année courante dans la variable $anneeCourante :

 

<xsl:variable name="anneeCourante" select="@Annee"/>

 

On vérifie ensuite que c’est la première fois que l’on rencontre cette année :

 

<xsl:if  test="not(preceding-sibling::FILM[@Annee=$anneeCourante])">

ß si le frère précédent qui est du type FILM n’a pas une année qui est égale à l’année courante

 

Si le test est concluant, on affiche cette année ainsi que le titre et le genre de tous les films de cette même année.

 

Vous pouvez voir le fichier GroupVariable.xsl

 

q       En vous inspirant de ce qui précède, donner le nombre de genres distincts qu’il y a ?