Copyright © 2017-2022 Yves MARCOUX; dernière modification de cette page: 2022-03-12.

INU3011 Documents structurés

Exemple de jointure en XSLT

Yves MARCOUX - EBSI - Université de Montréal


Introduction

Suppposons qu’un document XML contienne de l’information sur des vols aériens, sous la forme suivante:

<vols>
	 <vol code="AC234" aérDép="YUL" aérArr="LAX" />
	 <vol code="KL297" aérDép="AMS" aérArr="YUL" />
	 <vol code="UN456" aérDép="LAX" aérArr="YQB" />
	 <vol code="SK234" aérDép="BGO" aérArr="LAX" />
	 <vol code="AC111" aérDép="YUL" aérArr="YQB" />
</vols>

Comme on peut voir, les désignations d’aéroport de départ et de destination des vols sont données par les codes conventionnels de trois lettres habituels (voir par exemple <http://www.airportcodes.org/>).

La feuille de style XSLT suivante permettrait d’effectuer un formatage minimal de l’information:

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

<xsl:template match="/">
  <html>
  <head>
    <title>Liste de vols aériens</title>
  </head>
  <body>
    <dl>
      <xsl:apply-templates />
    </dl>
  </body>
  </html>
</xsl:template>

<xsl:template match="vol">
  <dt><xsl:value-of select="@code" /></dt>
  <dd>De: <xsl:value-of select="@aérDép" /></dd>
  <dd>À: <xsl:value-of select="@aérArr" /></dd>
</xsl:template>

</xsl:stylesheet>

Cette feuille de style, appliquée à notre document XML, produira l’extrant HTML suivant:

<html>
<head>
  <title>Liste de vols aériens</title>
</head>
<body>
  <dl>
    <dt>AC234</dt>
    <dd>De: YUL</dd>
    <dd>À: LAX</dd>
    <dt>KL297</dt>
    <dd>De: AMS</dd>
    <dd>À: YUL</dd>
    <dt>UN456</dt>
    <dd>De: LAX</dd>
    <dd>À: YQB</dd>
    <dt>SK234</dt>
    <dd>De: BGO</dd>
    <dd>À: LAX</dd>
    <dt>AC111</dt>
    <dd>De: YUL</dd>
    <dd>À: YQB</dd>
  </dl>
</body>
</html>

Cet extrant HTML, en navigateur Web, s’affichera essentiellement comme suit:

AC234
De: YUL
À: LAX
KL297
De: AMS
À: YUL
UN456
De: LAX
À: YQB
SK234
De: BGO
À: LAX
AC111
De: YUL
À: YQB

(Bien sûr, pour que le lien soit fait entre le document XML et la feuille de style, il faudrait ajouter l’instruction de traitement suivante au début du document XML:

<?xml-stylesheet type="text/xsl" href="vols.xsl" ?>

vols.xsl est le nom du fichier de la feuille XSLT, en supposant qu’elle soit dans le même dossier que le fichier XML.)


Expansion des codes

Supposons maintenant que l’on veuille présenter les noms complets des aéroports plutôt que les codes dans notre affichage du document.

Évidemment, il faut pour cela disposer minimalement d’une table de correspondance qui donne la forme développée de chaque code. Supposons que l’on dispose d’une telle table, sous forme d’un document XML (distinct du document XML principal et de la feuille de style) de la forme suivante:

<codesAér>
	 <aér code="YUL" nom="Montréal Trudeau" />
	 <aér code="YQB" nom="Québec Jean-Lesage" />
	 <aér code="BGO" nom="Bergen Flesland" />
	 <aér code="AMS" nom="Amsterdam Schiphol" />
	 <aér code="LAX" nom="Los Angeles International" />
</codesAér>

On voudrait donc un affichage de nos vols dans lequel les noms complets des aéroports, selon cette table de correspondance, sont affichés au lieu des codes. Ainsi, le premier vol dans notre liste serait affiché comme ceci:

AC234
De: Montréal Trudeau
À: Los Angeles International

plutôt que:

AC234
De: YUL
À: LAX

parce que « Montréal Trudeau » est le nom développé de l’aéroport dont le code est « YUL » et « Los Angeles International » est la forme développée du code « LAX ».

Il se trouve qu’une toute petite modification dans le gabarit pour l’élément vol permet d’obtenir ce que l’on veut.

Notons que le remplacement d’un code par un intitulé complet pour fins d’affichage peut être réalisé par une suite d’instructions conditionnelles <xsl:if> ou de gabarits spécialisés qui contiennent directement les intitulés de remplacement. Cependant, la méthode que nous présentons ci-après est plus légère et permet de tirer avantage du fait que la table de correspondance existe sous la forme d’un document XML distinct de la feuille de style.

Le remplacement d’une valeur par une valeur correspondante trouvée dans une table de correspondance correspond à une forme simple de l’opération de jointure du modèle relationnel de bases de données.


La fonction XPath/XSLT document()

La solution utilise une fonction XPath/XSLT qui permet d’accéder des éléments XML situés dans un autre document XML que celui en cours de traitement: la fonction document(). Lorsqu’une expression XPath commence par un appel à cette fonction, il s’agit d’une expression absolue (comme celles commençant par « / ») dont le point de départ est la racine d’un document XML quelconque. Le document en question est identifié par son nom de fichier (en fait, en général, son URI), qui est passé en paramètre à la fonction document().

Par exemple, si le document XML ci-dessus donnant la table de correspondance entre les codes et les noms d’aéroport porte le nom de fichier codesAer.xml (que l’on suppose dans le même dossier que la feuille de style), alors l’expression XPath document('codesAer.xml') désigne la racine du document en question. Alors, l’instruction XSLT:

<xsl:value-of select="document('codesAer.xml')" />

n’importe où dans un gabarit de feuille de style, donnerait le contenu textuel débalisé de tout le document codesAer.xml. Comme le contenu débalisé de ce document est la chaîne vide (il n’y a rien d’autre que des balises dans le document), cette instruction telle quelle ne serait pas intéressante. Par contre, on peut la prolonger de façon à extraire de l’information utile du document. Ainsi, cette nouvelle expression XSLT:

<xsl:value-of select="document('codesAer.xml')//*[@code='YUL']/@nom" />

retournera toujours comme extrant la valeur de l’attribut nom du premier élément possédant un attribut code égal à YUL, soit Montréal Trudeau.

Intuitivement, on peut voir la fonction document comme une façon d’écrire des chemins d’accès absolus qui commencent encore plus haut que la racine du document courant: ils commencent en fait dans le système de fichiers (ou dans Internet dans le cas d’URI en général)!

Évidemment, dans notre cas, nous ne voulons pas extraire le nom correspondant à un code d’aéroport fixe connu à l’avance, nous voulons extraire celui qui correspond à la valeur d’un des attributs aérDép et aérArr du vol que l’on est en train de traiter.

Traitons d’abord le cas de l’attribut aérDép. Au lieu de comparer l’attribut code à une chaîne fixe, comme dans [@code='YUL'], nous allons le comparer à la valeur de l’attribut aérDép du noeud (vol) courant, comme ceci: [@code=current()/@aérDép].

L’expression XPath complète permettant d’aller chercher le nom de l’aéroport de départ sera alors:

<xsl:value-of select="document('codesAer.xml')//*[@code=current()/@aérDép]/@nom" />

Cette solution utilise une autre fonction XPath/XSLT dont nous n’avons pas encore parlé: la fonction current(). Cette fonction permet de s’assurer que l’attribut dont la valeur est comparée à celle de code est bien l’attribut aérDép du noeud courant (celui pour lequel le gabarit s’exécute). Si on n’utilisait pas cette fonction, autrement dit si le filtre était [@code=@aérDép] au lieu de [@code=current()/@aérDép], le processeur XSLT comparerait les attributs code et aérDép du même élément dans le document codesAer.xml. Comme aucun élément dans ce document n’a d’attribut aérDép, le filtre ne retournerait jamais aucun élément, et le xsl:value-of dans son ensemble retournerait toujours un extrant vide.


La solution complète

La solution complète consiste à modifier légèrement le gabarit pour l’élément vol. Il suffit de remplacer les deux instructions xsl:value-of utilisées pour aller chercher les codes d’aéroport de départ et d’arrivée par des instructions xsl:value-of allant chercher les noms développés correspondant aux codes. Cela nous donne le nouveau gabarit suivant:

<xsl:template match="vol">
  <dt><xsl:value-of select="@code" /></dt>
  <dd>De: <xsl:value-of select="document('codesAer.xml')//*[@code=current()/@aérDép]/@nom" /></dd>
  <dd>À: <xsl:value-of select="document('codesAer.xml')//*[@code=current()/@aérArr]/@nom" /></dd>
</xsl:template>

Avec ce nouveau gabarit, la feuille de style produit maintenant cet extrant :

<html>
<head>
  <title>Liste de vols aériens</title>
</head>
<body>
  <dl>
    <dt>AC234</dt>
    <dd>De: Montréal Trudeau</dd>
    <dd>À: Los Angeles International</dd>
    <dt>KL297</dt>
    <dd>De: Amsterdam Schiphol</dd>
    <dd>À: Montréal Trudeau</dd>
    <dt>UN456</dt>
    <dd>De: Los Angeles International</dd>
    <dd>À: Québec Jean-Lesage</dd>
    <dt>SK234</dt>
    <dd>De: Bergen Flesland</dd>
    <dd>À: Los Angeles International</dd>
    <dt>AC111</dt>
    <dd>De: Montréal Trudeau</dd>
    <dd>À: Québec Jean-Lesage</dd>
  </dl>
</body>
</html>

qui, en navigateur, produira un affichage ressemblant à:

AC234
De: Montréal Trudeau
À: Los Angeles International
KL297
De: Amsterdam Schiphol
À: Montréal Trudeau
UN456
De: Los Angeles International
À: Québec Jean-Lesage
SK234
De: Bergen Flesland
À: Los Angeles International
AC111
De: Montréal Trudeau
À: Québec Jean-Lesage

En guise de conclusion

La solution présentée ci-dessus a l’avantage de la simplicité. Il existe cependant plusieurs autres façons de procéder. L’utilisation de variables, notamment, permettrait d’alléger quelque peu les expressions XPath. Le lecteur intéressé à en apprendre plus sur les variables peut consulter l’ouvrage de Michael Kay mentionné dans la bibliographie du cours.

La mécanique des jointures peut être poussée plus loin et inclure l’interrogation de services Web dynamiques, par exemple. L’exemple présenté dans le dossier _conversion-devise illustre cette possibilité, en interrogeant dynamiquement un service en ligne de taux de change de la Banque du Canada pour convertir en dollars canadiens des prix exprimés en euros, en utilisant le taux de change courant. Cet exemple ne fonctionne correctement qu’avec Firefox.