Introduction à XSLT
Transcription
Introduction à XSLT
Introduction à XSLT Introduction Dans cette démonstration, nous allons aborder la conception et l’utilisation des feuilles de style. Les feuilles de style permettent de transformer un document xml vers un autre document xml, un document html ou un document de texte brut. Premiers pas Une première feuille de style que nous allons faire transformera un document xml contenant des cours et des inscriptions en un document texte listant les cours. Nous voudrions donc que le fichier inscriptions.xml soit transformer vers le texte suivant ift3220 ift2030 Pour ce faire nous allons commencer par créer une feuille de style xslt version 2.0 à l’aide de XmlSpy. Une feuille de style vierge devrait avoir l’allure suivante <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> </xsl:stylesheet> Cette feuille de style ne fait encore rien. Si nous l’utilisons pour transformer le document inscriptions.xml, nous obtenons le document suivant (sans les fins de ligne): <?xml version=“1.0”encoding=“UTF-8”?> lepb12345678letp12345678lesg12345678tenc12345678 krac12345678lepb12345678lecs12345678krac12345678 Que s’est-il passé? Premièrement, la ligne <?xml... est présente car nous avons la balise <xsl:output spécifie que nous voulons créer un document xml. Ce n’est pas le cas, nous allons donc remplacer la balise <xsl:output ...> par la suivante <xsl:output method="text" encoding="UTF-8"/> Si nous réessayons, nous obtenons lepb12345678letp12345678lesg12345678tenc12345678 krac12345678lepb12345678lecs12345678krac12345678 1 C’est mieux, mais ce n’est pas encore ça. Les codes permanents des élèves inscrits sont affichés sans que nous n’ayons rien demandé. C’est parce que par défaut, le texte des élément est affiché et le texte des attributs ne l’est pas. Nous allons donc spécifier un comportement différent de celui par défaut qui, pour chaque cours, affichera son identificateur sans afficher les élèves inscrits. Nous allons tout d’abord importer l’espace de nommage que nous voulons utiliser en ajoutant l’attribut suivant à la balise xsl:stylesheet xmlns:i="http://www-etud.iro.umontreal.ca/~patryale/demo10/inscriptions/" Nous pourrons maintenant utiliser les éléments du schéma cours.xsd en les préfixant de i:. Nous allons ensuite changer le comportement par défaut pour le traitement de la balise i:cours en ajoutant le code suivant avant la balise fermante </xsl:stylesheet> <xsl:template match="/i:inscriptions/i:cours"> <xsl:value-of select="@id"/> </xsl:template> Si nous utilisons notre nouvelle feuille de style, nous obtenons la sortie suivante ift3220ift2030 Nous y sommes presque, il ne nous reste qu’à ajouter le saut-de-ligne après chaque sigle de cours. Pour ce faire, après l’instruction <xsl::value-of ..., nous allons ajouter l’instruction suivante <xsl:text> </xsl:text> Cette instruction fait afficher le caractère unicode ayant le code 10 (retour de ligne) dans le texte à construire. Nous obtenons maintenant ift3220 ift2030 la liste des cours que nous convoitions. Un autre petit pas C’est bien de pouvoir lister les cours, mais il serait encore mieux de lister les élèves dans chacun de ces cours. Nous allons donc spécifier un traitement spécifique pour chacun des inscrits <xsl:template match="/i:inscriptions/i:cours/i:inscription"> <xsl:value-of select="."/> <xsl:text> </xsl:text> </xsl:template> Maintenant, si nous réexécutons notre feuille de style, nous devrions avoir aussi la liste des élèves, non? Essayons .... Que s’est-il passé, pourquoi rien n’a changé? C’est tout simplement parce que lorsqu’un cours est traı̂té, il ne propage pas le traitement à ses enfants. Pour propager le traitement aux enfants, nous allons remplacer la balise qui traitait les cours par <xsl:template match="/i:inscriptions/i:cours"> <xsl:value-of select="@id"/> <xsl:apply-templates/> </xsl:template> Nous devrions maintenant nous retrouver avec le document que nous voulions 2 ift3220 lepb12345678 letp12345678 lesg12345678 tenc12345678 krac12345678 ift2030 lepb12345678 lecs12345678 krac12345678 C’est bien, mais ce n’est pas très jolie. Nous pourrions sûrement mieux formatter le tout. Nous pourrions par exemple faire une en-tête pour chaque cours. Allons-y donc et remplaçons le code traitant l’élément i:cours par le suivant <xsl:template match="/i:inscriptions/i:cours"> <xsl:value-of select="fn:replace(@id, ’.’, ’-’)"/> <xsl:text> </xsl:text> <xsl:value-of select="@id"/> <xsl:text> </xsl:text> <xsl:value-of select="fn:replace(@id, ’.’, ’-’)"/> <xsl:text> </xsl:text> <xsl:apply-templates/> <xsl:text> </xsl:text> </xsl:template> Nous obtenons maintenant la sortie suivante ------ift3220 ------lepb12345678 letp12345678 lesg12345678 tenc12345678 krac12345678 ------ift2030 ------lepb12345678 lecs12345678 krac12345678 Ça semble bon, mais qu’avons nous fait au juste? Nous avons tout simplement utilisé la fonction replace (déclarée dans l’espace de nommage fn) pour remplacer chaque caractère (expression régulière .) du nom du cours par un -. Ce qui compte Maintenant que nous avons afficher la liste des élèves de chaque cours, nous voulons avoir le nombre d’élève inscrit après le code du dernier élève. Pour ce faire, nous utiliserons la fonction count 3 <xsl:template match="/i:inscriptions/i:cours"> <!-- ... --> <xsl:apply-templates/> <xsl:text>Nombre d’inscrits: </xsl:text> <xsl:value-of select="count(i:inscription)"/> <xsl:text> </xsl:text> <xsl:text> </xsl:text> </xsl:template> La fonction count prend en paramètre une expression XPath et retourne le nombre de noeuds auxquels l’expression fait référence. Nous obtenons maintenant la sortie suivante ------ift3220 ------lepb12345678 letp12345678 lesg12345678 tenc12345678 krac12345678 Nombre d’inscrits: 5 ------ift2030 ------lepb12345678 lecs12345678 krac12345678 Nombre d’inscrits: 3 À l’ordre Mettons un peu d’ordre dans tout cela. Nous aimerions pouvoir trier les élèves des différents cours par ordre de leur code permanent. Pour ce faire, nous allons spécifier un critère de tri dans la balise apply-templates <xsl:apply-templates> <xsl:sort select="."/> </xsl:apply-templates> Cela à pour effet de traiter les noeuds enfants de cours par ordre de inscription. Notez que la racine du critère de tri est l’élément enfant de cours. Pouvez-vous maintenant ordonner la liste par ordre de cours? Les listes peuvent être jolies Dans les exercices précédents, nous avons affiché du texte brut. Ici, nous allons reprendre l’exemple précédent et l’afficher en XHTML <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" 4 xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" xmlns:i="http://wwwetud.iro.umontreal.ca/~patryale/demo10/inscriptions/" > <xsl:output method="xml" doctype-public=" -// W3C // DTD XHTML 1.0 Strict // EN" doctype-system="http: // www.w3.org/TR/ xhtml1 /DTD/xhtml1 strict .dtd" indent ="yes" encoding="UTF-8"/> <xsl:template match="/"> <html> <head> <title>Liste des cours</title> </head> <body> <h1>Liste des cours</h1> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match="/i:inscriptions/i:cours"> <h2><xsl:value-of select="@id"/></h2> <ul> <xsl:apply-templates> <xsl:sort select="."/> </xsl:apply-templates> </ul> <em> <xsl:text>Nombre d’inscrits: </xsl:text> <xsl:value-of select="count(i:inscription)"/> </em> </xsl:template> <xsl:template match="/i:inscriptions/i:cours/i:inscription"> <li><xsl:value-of select="."/></li> </xsl:template> </xsl:stylesheet> Le petit truc à remarquer est que les balises qui ne sont pas dans l’espace de noms xsl sont copiées à la sortie. Sinon, le code parle de lui-même. Il faut se souvenir que le parcourt de l’arbre se produit aux appels de apply-templates. Nommons les choses par leur nom Un code permanent n’est pas nécessairement très parlant pour un humain. Nous allons maintenant aller chercher le nom de chaque élève dans le document eleves.xsd. Pour ce faire, nous allons charger le document dans une variable, en ajoutant le code suivant après la balise <output... de notre document <xsl:variable name="eleves" select="document(’eleves.xml’)"/> 5 Ensuite, pour pouvoir utiliser les balises du documents chargé, nous allons ajouter la déclaration d’espace de nom suivante : xmlns:e=“http://www-etud.iro.umontreal.ca/˜patryale/demo10/eleves/” Finalement, nous allons remplacer notre code traitant chaque inscription par <xsl:template match="/i:inscriptions/i:cours/i:inscription"> <xsl:variable name="id" select="text()"/> <xsl:variable name="eleve" select="$eleves/e:eleves/e:eleve[@id=$id]"/> <li> <xsl:value-of select="$eleve/e:prenom"/> <xsl:text> </xsl:text> <xsl:value-of select="$eleve/e:nom"/> </li> </xsl:template> Notez que pour pouvoir utiliser l’identificateur de la balise i:inscription dans le contexte de la balise e:eleve, nous utilisons une variable. Notez aussi qu’une variable peut contenir une chaı̂ne de caractères ou un sous-arbre. Si nous exécutons notre feuille de style, nous obtenons <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC " -// W3C // DTD XHTML 1.0 Strict // EN" "http: // www.w3.org/TR/ xhtml1 /DTD/xhtml1 - strict .dtd"> <html xmlns:i="http://wwwetud.iro.umontreal.ca/~patryale/demo10/inscriptions/" xmlns:e="http://www-etud.iro.umontreal.ca/~patryale/demo10/eleves/" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" xmlns:xs="http://www.w3.org/2001/XMLSchema" > <head> <title>Liste des cours</title> </head> <body> <h1>Liste des cours</h1> <h2>ift3220</h2> <ul> <li>Capitaine Krabs</li> <li>Bob L’Éponge</li> <li>Gary L’Escargot</li> <li>Patrick L’Étoile</li> <li>Carlo Tentacules</li> </ul> <em>Nombre d’inscrits: 5</em> <h2>ift2030</h2> <ul> <li>Capitaine Krabs</li> <li>Sandy L’Écureuil</li> <li>Bob L’Éponge</li> </ul> <em>Nombre d’inscrits: 3</em> 6 </body> </html> Pour éviter d’inclure les espaces de noms, nous pouvons ajouter l’attribut suivant à la balise stylesheet exclude-result-prefixes="xsl xs fn xdt c e" C’est ici que nous arrêtons avec cet exemple. Les balises XSLT Dans les exemples précédents, quelques balises XSLT d’intérêt sont les suivantes : output Spécifie le type de document produit (xml, texte ou html). template Spécifie un traitement pour un ensemble de noeud à l’aide de l’attribut match. apply-templates Continue le parcours dans les fils de l’élément traité. sort Spécifie l’ordre dans lequel les fils seront visités à l’aide de la balise select. text Insère du texte dans le document produit. value-of Évalue expression dans l’attribut select et retourne sa valeur. variable Déclare une variable constante. Nous avons aussi vu qu’il ne faut pas oublier de déclarer les espaces de noms dans la balise stylesheet. <?xml-stylesheet type="text/xsl" href="compactHTML.xsl"?> Les expressions XPath Vous avez remarqué que les expressions dans les attributs match ou select peuvent être très simple comme elle peuvent être complexes. Ces expressions sont appelées expressions XPath. Elles sont composées de trois composantes : 1. Un axe qui donne un chemin vers un ensemble de noeud, en séparant chaque nom de noeud par un /. Le noeud racine est nommé /, le noeud courant par . et le noeud parent par ... Pour indiquer une relation parent-enfant non-immédiate, on utilise le //. 2. Un noeud de test qui peut être un nom de balise, * ou un nom de fonction comme node() et text() 3. Un prédicat donné entre crochet après le test. Un prédicat peut utiliser une variable en préfixant son nom de $ ou une fonction comme concat, substring, count ou replace. Dans l’expression suivante $eleves/e:eleves/e:eleve[@id=$id] L’axe est $eleves/e:eleves/, le test e:eleve et le prédicat [@id=$id]. Pour plus d’informations, vous pouvez consulter Xml: Looking at the forest instead of the Trees. 7