Web XSLT Travaux Pratiques

Inria

Transformez un document XML en HTML depuis un serveur Web.

Prérequis

  • XSLT
  • CSS pour faire joli
  • Javascript pour se démarquer

Cours

  • Développement Web avec Java

Transformation avec XSLT

Zoo

On dispose du document XML :

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE zoo SYSTEM "zoo.dtd" [
    <!ENTITY % notations SYSTEM "notations.ent">
%notations;
    <!ENTITY flipper.jpg SYSTEM "flipper.jpg" NDATA jpg>
    <!ENTITY oum.jpg SYSTEM "oum.jpg" NDATA jpg>
    <!ENTITY ecco.jpg SYSTEM "ecco.jpg" NDATA jpg>
]>
<!-- Un petit Zoo avec quelques animaux -->
<zoo xmlns="urn:unice:minfo-2004" xmlns:doc="http://www.unice.fr/minfo/2004">
    <doc:info>Ouvert tous les jours de 8h00 à 19h00</doc:info>
    <doc:attention>Il est <doc:b>interdit</doc:b> de nourrir les animaux.</doc:attention>
    <aquarium>
        <mammifères-marins>
            <dauphins>
                <dauphin id="jhgtr13" photo="flipper.jpg" date-naissance="1997-4-1">
                    <nom>Flipper</nom>
                    <sexe>M</sexe>
                    <taille unité="cm">215</taille>
                    <poids unité="kg">105</poids>
                </dauphin>
                <dauphin id="lkjh45" photo="ecco.jpg" date-naissance="2003-10-23">
                    <nom>Ecco</nom>
                    <sexe>F</sexe>
                    <taille unité="cm">202</taille>
                    <poids unité="kg">98</poids>
                </dauphin>
                <dauphin id="kjlhy90" photo="oum.jpg" date-naissance="1996-12-25">
                    <nom>Oum</nom>
                    <sexe>F</sexe>
                    <!-- c'est mon préféré -->
                    <taille unité="cm">295</taille>
                    <poids unité="kg" status="mesure approximative">190<!-- il faut refaire la pesée --></poids>
                </dauphin>
            </dauphins>
        </mammifères-marins>
        <poissons>
            <sélaciens>
                <doc:danger>Il est <doc:b>interdit</doc:b> de nager avec les requins.</doc:danger>
                <requin id="plojk09" espèce="marteau" date-naissance="1998-6-5">
                    <nom>Oussama</nom>
                    <sexe>M</sexe>
                    <taille unité="cm">455</taille>
                    <poids unité="kg">540</poids>
                    <doc:commentaire>A tendance a se jeter contre les murs
                        <![CDATA[<<< marteau & cinglé !!! >>>]]>
                        <!-- Il faudra bien le faire piquer un jour -->
                    </doc:commentaire>
                </requin>
                <requin id="vgyuh43" espèce="requin bleu" nom-savant="carcharias glaucus" date-naissance="2004-1-13">
                    <nom>Saddam</nom>
                    <sexe>M</sexe>
                    <taille unité="cm">355</taille>
                    <poids unité="kg" status="">425</poids>
                </requin>
            </sélaciens>
        </poissons>
    </aquarium>
</zoo>

et de sa DTD :

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

<!ENTITY nourrir.gif SYSTEM "nourrir.gif" NDATA gif>

<!ENTITY % block "doc:p | doc:info | doc:danger | doc:attention">
<!ENTITY % inline "doc:b | doc:i | doc:em | doc:strong | doc:stabilo | doc:img">
<!ENTITY % attractions "aquarium?, vivarium?, enclos?, volière?">
<!ENTITY % animal "nom, sexe, taille, poids, doc:commentaire?">
<!ENTITY % animal-attr "
    id		ID #REQUIRED
    espèce		CDATA #IMPLIED
    nom-savant	CDATA #IMPLIED
    photo		ENTITY #IMPLIED
    date-naissance  CDATA #REQUIRED
">
<!ENTITY % u-attr "
    unité		CDATA #REQUIRED
    status		CDATA #IMPLIED
">

<!ELEMENT zoo ((%block;)*, %attractions;)>
<!ATTLIST zoo 
  xmlns CDATA #FIXED "urn:unice:minfo-2004" 
  xmlns:doc CDATA #FIXED "http://www.unice.fr/minfo/2004">

<!ELEMENT doc:p (#PCDATA | %inline;)*>
<!ELEMENT doc:info (#PCDATA | %inline;)*>
<!ELEMENT doc:danger (#PCDATA | %inline;)*>
<!ELEMENT doc:attention (#PCDATA | %inline;)*>

<!ELEMENT doc:b (#PCDATA | %inline;)*>
<!ELEMENT doc:i (#PCDATA | %inline;)*>
<!ELEMENT doc:em (#PCDATA | %inline;)*>
<!ELEMENT doc:strong (#PCDATA | %inline;)*>
<!ELEMENT doc:stabilo (#PCDATA | %inline;)*>
<!ELEMENT doc:img EMPTY>
<!ATTLIST doc:img src ENTITY #REQUIRED>

<!ELEMENT aquarium (mammifères-marins?, poissons?)>
<!ELEMENT mammifères-marins (dauphins?, baleines?, orques?)>
<!ELEMENT poissons (sélaciens?)>

<!ELEMENT vivarium (serpents?, arachnéens?)>
<!ELEMENT serpents (boas?)>
<!ELEMENT arachnéens (mygales?)>

<!ELEMENT enclos (primates?, fauves?)>
<!ELEMENT primates (gorilles?, orangs-outans?)>
<!ELEMENT fauves (tigres?, lions?)>

<!ELEMENT volière (oiseaux-terrestres?, oiseaux-aquatiques?, rapaces?)>
<!ELEMENT oiseaux-terrestres (autruches?)>
<!ELEMENT oiseaux-aquatiques (pingouins?, manchots?)>
<!ELEMENT rapaces (aigles?)*>

<!ELEMENT dauphins (%block; | dauphin)+>
<!ELEMENT dauphin (%animal;)>
<!ATTLIST dauphin %animal-attr;>

<!ELEMENT baleines (%block; | baleine)+>
<!ELEMENT baleine (%animal;)>
<!ATTLIST baleine %animal-attr;>

<!ELEMENT orques (%block; | orque)+>
<!ELEMENT orque (%animal;)>
<!ATTLIST orque %animal-attr;>

<!ELEMENT sélaciens (%block; | requin)+>
<!ELEMENT requin (%animal;)>
<!ATTLIST requin %animal-attr;>

<!ELEMENT boas (%block; | boa)+>
<!ELEMENT boa (%animal;)>
<!ATTLIST boa %animal-attr;>

<!ELEMENT mygales (%block; | mygale)+>
<!ELEMENT mygale (%animal;)>
<!ATTLIST mygale %animal-attr;>

<!ELEMENT gorilles (%block; | gorille)+>
<!ELEMENT gorille (%animal;)>
<!ATTLIST gorille %animal-attr;>

<!ELEMENT orangs-outans (%block; | orang-outan)+>
<!ELEMENT orang-outan (%animal;)>
<!ATTLIST orang-outan %animal-attr;>

<!ELEMENT tigres (%block; | tigre)+>
<!ELEMENT tigre (%animal;)>
<!ATTLIST tigre %animal-attr;>

<!ELEMENT lions (%block; | lion)+>
<!ELEMENT lion (%animal;)>
<!ATTLIST lion %animal-attr;>

<!ELEMENT autruches (%block; | autruche)+>
<!ELEMENT autruche (%animal;)>
<!ATTLIST autruche %animal-attr;>

<!ELEMENT pingouins (%block; | pingouin)+>
<!ELEMENT pingouin (%animal;)>
<!ATTLIST pingouin %animal-attr;>

<!ELEMENT manchots (%block; | manchot)+>
<!ELEMENT manchot (%animal;)>
<!ATTLIST manchot %animal-attr;>

<!ELEMENT aigles (%block; | aigle)+>
<!ELEMENT aigle (%animal;)>
<!ATTLIST aigle %animal-attr;>

<!ELEMENT nom (#PCDATA)>
<!ELEMENT sexe (#PCDATA)>
<!ELEMENT taille (#PCDATA)>
<!ATTLIST taille %u-attr;>

<!ELEMENT poids (#PCDATA)>
<!ATTLIST poids %u-attr;>

<!ELEMENT doc:commentaire (#PCDATA | %block;)*>

qui utilise une entité :

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!NOTATION jpg SYSTEM "images/jpeg">
<!NOTATION gif SYSTEM "images/gif">

Le document XML décrit les animaux d'un Zoo. L'objectif est de faire une application Web qui transforme le document source XML en une version publiée en (X)HTML avec XSLT. Tout se passe côté serveur (dans un précédent tépé, tout se passait côté client). La différence est qu'on va avoir une plus grande maîtrise de la transformation, en passant des paramètres à la feuille de style par exemple. Il faut donc réaliser une application Web avec Java et XML/XSLT (JAXP/TRAX).

Installez Tomcat.

Pour commencer avec Java et XSLT

Créez une servlet qui instancie un template XSLT avec cette feuille de style :

<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:zoo="urn:unice:minfo-2004"
    xmlns:doc="http://www.unice.fr/minfo/2004"
    xmlns:date="*** Processing dates ***"
    exclude-result-prefixes="zoo doc date">

    <xsl:import href="dates.xsl"/>
    <xsl:output method="html"/>
    <xsl:param name="liste-par-nom" select="false()" />

    <xsl:template match="/">
        <html>
            <head>
                <title>Zoo</title>
                <link href="zoo.css" rel="stylesheet" type="text/css"/>
            </head>
            <body>
                <h1>Bienvenue au Zoo</h1>
                <xsl:choose>
                    <xsl:when test="$liste-par-nom">
                        <ul>
                            <xsl:apply-templates mode="toc" select="//zoo:nom">
                                <xsl:sort select="."/>
                            </xsl:apply-templates>
                        </ul>
                    </xsl:when>
                    <xsl:otherwise>
                        <ul>
                            <xsl:apply-templates mode="toc" select="//zoo:nom">
                                <xsl:sort select="../@date-naissance"/>
                            </xsl:apply-templates>
                        </ul>
                    </xsl:otherwise>
                </xsl:choose>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="zoo:nom" mode="toc">
        <li><a href="#{generate-id()}"><xsl:value-of select="."/></a></li>
    </xsl:template>

    <xsl:template match="doc:info|doc:attention|doc:danger">
        <xsl:choose>
            <xsl:when test="parent::zoo:dauphins or parent::zoo:sélaciens">
                <tr>
                    <td colspan="6"><xsl:call-template name="doc:avertissement"/></td>
                </tr>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="doc:avertissement"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="doc:avertissement">
	<table class="{local-name()}" align="center">
            <tr>
                <td><img src="img/{local-name()}.gif" /></td>
                <td><xsl:apply-templates /></td>
            </tr>
	</table>
    </xsl:template>

    <xsl:template match="doc:b"><b><xsl:apply-templates /></b></xsl:template>
    <xsl:template match="doc:i"><i><xsl:apply-templates /></i></xsl:template>
    <xsl:template match="doc:em"><em><xsl:apply-templates /></em></xsl:template>
    <xsl:template match="doc:strong"><strong><xsl:apply-templates /></strong></xsl:template>

    <xsl:template match="zoo:dauphins|zoo:sélaciens">
        <h2><xsl:value-of select="local-name()" /></h2>
        <table border="1">
            <tr>
                <th>Nom</th>
                <th>Date naissance</th>
                <th>Type</th>
                <th>Taille</th>
                <th>Poids</th>
                <th>Sexe</th>
            </tr>
            <xsl:apply-templates/>
        </table>
    </xsl:template>

    <xsl:template match="zoo:dauphin|zoo:requin">
	<tr>
            <td><a name="{generate-id(zoo:nom)}"><xsl:value-of select="zoo:nom" /></a></td>
            <td>
                <xsl:call-template name="date:format">
                    <xsl:with-param name="date" select="@date-naissance"/>
                </xsl:call-template>
            </td>
            <td><xsl:value-of select="@espèce" /><br /><i><xsl:value-of select="@nom-savant" /></i></td>
            <td><xsl:value-of select="zoo:taille" />&#160;<xsl:value-of select="zoo:taille/@unité" /></td>
            <td><xsl:value-of select="zoo:poids" />&#160;<xsl:value-of select="zoo:poids/@unité" /></td>
            <td><xsl:value-of select="zoo:sexe" /></td>
            <xsl:if test="@photo">
                <td><img src="img/{@photo}" alt="{zoo:nom}" /></td>
            </xsl:if>
	</tr>
    </xsl:template>

</xsl:stylesheet>

cette autre :

<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:date="*** Processing dates ***">

    <date:month-names>
        <date:month short="jan">janvier</date:month>
        <date:month short="fév">février</date:month>
        <date:month short="mar">mars</date:month>
        <date:month short="avr">avril</date:month>
        <date:month short="mai">mai</date:month>
        <date:month short="jun">juin</date:month>
        <date:month short="jul">juillet</date:month>
        <date:month short="aou">août</date:month>
        <date:month short="sep">septembre</date:month>
        <date:month short="oct">octobre</date:month>
        <date:month short="nov">novembre</date:month>
        <date:month short="déc">décembre</date:month>
    </date:month-names>

    <xsl:template name="date:month-name">
        <!--returns the name of the month from its number-->
        <xsl:param name="month" select="0"/>

        <xsl:value-of select="document('')/*/date:month-names/date:month[$month]"/>
    </xsl:template>

    <xsl:template name="date:format">
        <!--formatting dates "j/m/aaaa" or "aaaa-m-j" -->
        <xsl:param name="date" select="''"/>
        <xsl:param name="day" select="substring-before($date, '/')"/>
        <xsl:param name="month" select="substring-before(substring-after($date, '/'), '/')"/>
        <xsl:param name="year" select="substring-after(substring-after($date, '/'), '/')"/>

        <xsl:choose>
            <xsl:when test="not(contains($date, '-'))">
                <xsl:variable name="month-name">
                    <xsl:call-template name="date:month-name">
                        <xsl:with-param name="month" select="number($month)"/>
                    </xsl:call-template>
                </xsl:variable>
                <xsl:text/>
                <xsl:value-of select="$day" />
                <xsl:text>&#160;</xsl:text>
                <xsl:value-of select="$month-name" />
                <xsl:text>&#160;</xsl:text>
                <xsl:value-of select="$year" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="date:format">
                    <xsl:with-param name="year" select="substring-before($date, '-')"/>
                    <xsl:with-param name="month" select="substring-before(substring-after($date, '-'), '-')"/>
                    <xsl:with-param name="day" select="substring-after(substring-after($date, '-'), '-')"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="date:julian-day">
        <!--returns the julian day from the date-->
        <xsl:param name="date" select="''"/>
        <xsl:param name="year" select="substring-before($date, '-')"/>
        <xsl:param name="month" select="substring-before(substring-after($date, '-'), '-')"/>
        <xsl:param name="day" select="substring-after(substring-after($date, '-'), '-')"/>

        <xsl:variable name="a" select="floor( ( 14 - $month ) div 12 )"/>
        <xsl:variable name="y" select="$year + 4800 - $a"/>
        <xsl:variable name="m" select="$month + 12 * $a - 3"/>
        <xsl:value-of select="$day + floor( ( 153 * $m + 2 ) div 5 ) + $y * 365 + floor( $y div 4 ) - floor( $y div 100 ) + floor( $y div 400) - 32045"/>
    </xsl:template>

    <xsl:template name="date:absolute-day">
        <!--returns the absolute day from the date-->
        <xsl:param name="date" select="''"/>
        <xsl:param name="year" select="substring-before($date, '-')"/>
        <xsl:param name="month" select="substring-before(substring-after($date, '-'), '-')"/>
        <xsl:param name="day" select="substring-after(substring-after($date, '-'), '-')"/>

        <xsl:variable name="julian">
            <xsl:call-template name="date:julian-day">
                <xsl:with-param name="year" select="$year"/>
                <xsl:with-param name="month" select="$month"/>
                <xsl:with-param name="day" select="$day"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$julian - 1721425"/>
    </xsl:template>
</xsl:stylesheet>

et ces ressources.

Appliquer la transformation au fichier XML, sérialiser directement vers la réponse pour le client.

On utilisera ce descripteur d'application Web :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">

<web-app>
    <display-name>Zoo</display-name>
    <description>Le Zoo de l'université de Nice</description>
    <servlet>
        <servlet-name>ZooServlet</servlet-name>
        <display-name>Simple servlet pour le Zoo</display-name>
        <description></description>
        <servlet-class>ZooServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/img</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.css</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>ZooServlet</servlet-name>
        <url-pattern>/zoo.html</url-pattern>
    </servlet-mapping>

    <mime-mapping>
        <extension>gif</extension>
        <mime-type>image/gif</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>jpg</extension>
        <mime-type>image/jpeg</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>css</extension>
        <mime-type>text/css</mime-type>
    </mime-mapping>

    <welcome-file-list>
        <welcome-file>zoo.html</welcome-file>
    </welcome-file-list>
</web-app>

et on se basera sur ce squelette Java :

/*
 * ZooServlet.java
 *
 * Created on March 20, 2004, 10:43 AM
 */

import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import java.io.*;
import org.w3c.dom.*;

/**
 * A servlet to display the animals.
 * @author Philippe Poulard
 */
public class ZooServlet extends HttpServlet {

    public final static String
        /** The path to the stylesheet. */
        XSLT_PATH = "WEB-INF/zoo.xsl",
        /** The path to the XML doc. */
        XML_PATH = "zoo.xml";

    /** Initializes the servlet.
     */
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    /** Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException {
        ServletContext webApp = this.getServletContext();
        try {




// JAXP stuff here




        } catch (Exception ex) {
            throw new ServletException( ex );
        }
    }

    /** Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, java.io.IOException {
        processRequest(request, response);
    }
    
    /** Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, java.io.IOException {
        processRequest(request, response);
    }
    
    /** Returns a short description of the servlet.
     */
    public String getServletInfo() {
        return "Zoo";
    }
    
}
Application Web à déployer dans Tomcat.

Organisez le code de votre servlet : la lecture de la feuille de style XSLT sera mise dans la méthode init() de votre servlet et sera partageable par l'application Web. La méthode doGet() se contentera de créer un template à utiliser à chaque requête. On pourra passer un paramètre à la requête pour obtenir un tri de la table des matières différent (par date de naissance ou par ordre alphabétique); il faudra transmettre un paramètre à la feuille de style.

Application Web à déployer dans Tomcat.

Contrôle de la granularité de la transformation

La liste globale des animaux par date de naissance ou par ordre alphabétique n'est pas une bonne idée. On va plutôt s'orienter vers un affichage sélectif.

On veut qu'une page JSP se charge de l'en-tête et du bas de page HTML. Créez la page JSP et modifiez la feuille de style et la servlet en conséquences; en particulier, on supprime le template qui matche la racine.

Application Web à déployer dans Tomcat.

On veut avoir un filtre sur les différentes espèces animales. Faites une autre feuille de style qui se contente de les lister. Chaque item de cette liste sera un lien qui permet de passer un paramètre que la servlet récupère et interprète pour se positionner sur le noeud correspondant qu'on transforme. De cette manière, seuls les animaux de l'espèce sélectionnée seront affichés.

Application Web à déployer dans Tomcat.