T.P. d initiation à XML avec l IDE eclipse : manipulation en Java Axe ISI - Philippe Beaune et Laurent Vercouter Jeudi 9 novembre 2006 matin / 3h Résumé Ce T.P. consiste en la découverte de différentes API Java qui permettent la manipulation de documents XML. Dans ce T.P., nous commencerons par découvrir l API SAX, puis le modèle DOM, et enfin l API JDOM. Dans chacune de ces parties, nous verrons comment valider un document XML, comment l exploiter et, dans un dernier temps, avec l API JDOM, comment effectuer des transformations XSL-T. Toutes ces manipulations seront faites en Java avec l IDE eclipse. 1 Objectif et moyens À l issue de ce T.P., vous devrez savoir utiliser l API JDOM (lecture, écriture et manipulation de documents XML, transformations XSL-T). Le passage préliminaire par l API SAX et le modèle DOM n est là que pour vous faire comprendre les bases de la manipulation de documents XML et l intérêt relatif de JDOM. Le logiciel eclipse est déjà installé dans les salles de T.P., aussi bien sous Linux que sous Windows, mais la version installée sous Windows est plus récente. Ce T.P. sera donc réalisé sous Windows afin d utiliser la même version d eclipse que pour les T.P. précédents. Dans la première partie de ce T.P., vous n aurez aucune installation logicielle à effectuer dans la mesure où l API JAXP fait partie de la distribution Java standard actuelle (JRE1.5) et contient l API SAX et le modèle DOM. Pour la deuxième partie du T.P. vous aurez besoin de l API JDOM. Vous n aurez qu à la télécharger et l installer sous eclipse. En fin de T.P. vous devrez envoyer un mél à Philippe.Beaune@emse.fr. 2 SAX, DOM, JDOM : en quelques mots Ce chapitre vous présente tout cela conceptuellement, mais très succinctement. Des références sont données pour les futurs approfondissements que vous serez inévitablement amenés à faire, un jour ou l autre, lors de vos développements de projet d axe. Cette partie ne doit vous prendre que 15 minutes. Le but 1
du T.P., ensuite, est de mettre tout cela en pratique. Puisque l API JAXP (et donc SAX et DOM) fait partie de la distribution Java standard, n oubliez pas, lorsque le besoin s en fait ressentir, d aller consulter la documentation Java de base. Soit vous en avez une version locale, soit vous pouvez aller à cette URL : http ://java.sun.com/j2se/1.5.0/docs/index.html. Pour ce qui est de la documentation de JDOM, elle est accessible ici : http ://www.jdom.org/docs/apidocs/. 2.1 L API SAX SAX est l acronyme de Simple API for XML. Il s agit d une API reposant sur un analyseur (parser) événementiel permettant de manipuler des documents XML. Pourquoi événementiel? Tout simplement parce que l analyseur de SAX va générer des événements au fur et à mesure de l avancée de son analyse (typiquement lorsqu il rencontrera une balise XML ouvrante ou fermante, mais ce n est qu un exemple). Les 2 implémentations les plus couramment utilisées de SAX sont Xerces et Crimson. De plus elles contiennent chaucune leur analyseur. Celui de Xerces est particulièrement réputé. Le coeur de SAX est composé principalement de 2 interfaces : XMLReader qui représente l analyseur, et ContentHandler qui reçoit les événements de l analyseur. Vous allez donc avoir d abord besoin d une implémentation de l interface XMLReader. Ensuite il vous suffira d invoquer la méthode parse() de ce lecteur XML pour que le processus d analyse soit lancé. L analyse du document XML génèrera donc des événements au fur et à mesure de l avancée de la lecture, ou bien des exceptions lorsque des erreurs de syntaxe seront détectées. Il vous faudra ensuite créer une instance de ContentHandler dans laquelle vous devrez décrire ce que vous souhaitez faire en réaction aux différents événements : au début du document, à la lecture d une balise ouvrante, à la lecture d une instruction,... Cette API est très légère et surtout peu consommatrice d espace mémoire puisqu elle ne mémorise pas le document XML en cours d analyse. C est au programmeur de définir ce qu il souhaite faire à la volée. Ce qui rendra donc laborieuse l implantation de traitements nécessitant par exemple la manipulation de plusieurs balises du document d origine (souvenez-vous, lors du précédent T.P. de la question : quelles sont les recettes contenant au plus 100g de beurre?). SAX conviendra donc bien aux gros documents XML, mais sera peu adaptée aux manipulations complexes nécessitant une vision d ensemble du document. Références : http ://www.cafeconleche.org/books/xmljava/chapters/ch06.html http ://java.sun.com/j2ee/1.4/docs/tutorial/doc/jaxpsax.html 2.2 Le modèle DOM DOM, (Document Object Model) est un modèle, une structure abstraite de données, pour représenter des documents XML sous forme d arborescences. Dif- 2
férentes interfaces du package org.w3c.dom (contenu dans l API JAXP) permettent de représenter les éléments XML, les attributs, les données, les commentaires,... Mais DOM n est pas lié à un langage de programmation particulier : c est un modèle abstrait défini par le W3C pour n importe quel langage. Le gros avantage de DOM est qu il permet d avoir en mémoire une représentation d un document XML sous forme d un arborescence d objets. Il conviendra donc pour les documents XML de taille raisonnable et pour lesquels les traitements nécessitent d avoir une vision d ensemble du document. Sachez aussi, même si nous ne rentrerons pas dans ces détails, que DOM est organisé en niveaux (et non pas en versions) : niveaux 1, 2 et 3 actuellement. La structure de base de DOM est une arborescence dont chaque sommet est instance de l interface org.w3c.dom.node. À partir de cette interface, DOM propose d autres interfaces dérivées plus spécifiques pour les éléments, les attributs, le texte,... DOM vous propose aussi des méthodes pour parcourir cette arborescence telles que getparent() ou getchildren() par exemple. DOM permet bien sûr ensuite de sérialiser cette arborescence pour écrire le document sur un flux de sortie. DOM propose même des modules de gestions d événements d ordre graphique (notamment gestion de la souris). Le lancement de l analyse d un document XML avec DOM se fait ainsi : après avoir créé un lecteur XML, vous n avez plus qu à appeler la méthode parse() de cet analyseur qui vous rend un document DOM (un objet org.w3c.dom.document). Références : http ://www.w3.org/dom/ http ://www.cafeconleche.org/books/xmljava/chapters/ch09.html 2.3 L API JDOM JDOM propose une manipulation de documents XML sous forme d arborescence. Mais elle se distingue de DOM par sa simplicité et sa légèreté. Néanmoins, bien qu elle ne respecte pas les spécifications DOM, elle est compatible avec DOM. JDOM ne contient aucun analyseur : elle utilise ceux déjà existants, notamment celui de SAX. Le but des créateurs de JDOM était de permettre une manipulation facile et efficace de documents XML en Java. En effet DOM a été conçu pour pouvoir être implanté dans n importe quel langage de programmation, et pas spécifiquement en Java. La principale critique de DOM est donc de ne pas tirer pleinement parti de toute la puissance de Java. Une autre critique est le fait que DOM doit aussi pouvoir représenter du HTML (avec ses imperfections, donc pas seulement du XML bien formé). Les créateurs de JDOM sont donc repartis de zéro, en concevant une API pour du XML pur et du Java pur. L API JDOM est aussi supposée être plus intuitive, donc moins succeptible d engendrer des erreurs de programmation. Pour créer une arborescence JDOM, soit vous le ferez à partir d un fichier XML, et alors vous utiliserez SAXBuilder, soit vous le ferez à partir d une ar- 3
borescence DOM, et alors vous utiliserez DOMBuilder. Vous pourrez bien sûr aussi le faire à partir de zéro en créant un document JDOM avec la classe org.jdom.document. En résumé, JDOM est une API pour facilement analyser, créer, manipuler et sérialiser des documents XML. Références http ://servlets.com/speaking/jdom-javaone.pdf http ://www.jdom.org/docs/apidocs/ http ://www.jdom.org/downloads/docs.html http ://www.cafeconleche.org/books/xmljava/chapters/ch14.html 3 Découverte de l API SAX Temps souhaité : 45 minutes. 3.1 Analyse simple d un fichier XML Sous eclipse, créez un nouveau projet Java, puis un package essaisax, et enfin une classe MonLecteurSax. Cette classe doit contenir une méthode main(). Dotez cette classe d une variable d instance privée lecteurxml de type XMLReader. Dans cette classe, créez un constructeur sans argument. Ce constructeur doit juste initialiser la variable lecteurxml. Pour cela, vous devez créer une instance de lecteur XMLReader, en appelant la méthode createxmlreader() de la classe XMLReaderFactory. Cette classe XMLReaderFactory est fournie dans l API JAXP, dans son module org.xml.sax.helpers : lecteurxml = XMLReaderFactory.createXMLReader() ; Si tout se passe bien, eclipse devrait déjà vous signaler une erreur : une histoire d exception non prise en main. À vous de résoudre cela. Ajoutez enfin à cette classe, une méthode analyse() qui prendra comme argument une chaine de caractères contenant l URI du fichier XML à analyser (soit xmluri cet argument). Le corps de cette méthode contient juste l appel de la méthode parse() de la variable privée lecteurxml : lecteurxml.parse(xmluri) ; Là encore, une histoire d exception non prise en main... La méthode main() peut maintenant ressembler à ça : try { MonLecteurSAX monlecteursax = new MonLecteurSAX(); MonLecteurSAX.analyse("recettes.xml"); 4
catch (Exception e) { System.err.println("y a un problème : " + e); Si vous compilez et exécutez ce programme, normalement il devrait vous signaler l absence du fichier recettes.xml. Allez récupérer ce fichier (cf. T.P. précédent), et relancez le programme. Il manque encore un fichier : la DTD déclarée dans le fichier recettes.xml. Récupérez cette DTD et relancez. Cette fois plus aucun problème, l exécution se passe bien mais elle est silencieuse. C est normal : l analyse du fichier XML a généré pleins d événements mais nous n avons pas encore programmé ce qu il fallait faire de ces événements. Pour cela vous devez vous créer une classe MonContentHandler, soit en implémentant l interface ContentHandler, soit en étendant la classe DefaultHandler (choisissez la première solution). Pour associer MonContentHandler à votre lecteur SAX, introduisez la ligne suivante dans le constructeur de MonLecteurSax : lecteurxml.setcontenthandler(new MonContentHandler()) ; Pour voir que tout ça fonctionne bien, dans la classe MonContentHandler, repérez la méthode startdocument() et faites-lui écrire quelque chose à l écran. Compilez et exécutez : vérifiez que cela produit quelque chose sur la console d eclipse. Ce qui s est passé : au début de l analyse du fichier XML, un événement a été généré et traité par startdocument(). Maintenant, en programmant quelque chose dans les méthodes characters(), startelement() et endelement(), vous allez faire afficher tous les titres de recettes contenus dans le fichier XML. Avant d aller plus loin, allez sur la documentation de org.xml.sax.contenthandler pour voir ce que sont les arguments de ces 3 méthodes. Pour réaliser cet affichage des titres, il suffit de dire à la méthode characters() d afficher une partie de son premier argument, mais uniquement si l événement characters a été généré lorsque l analyseur se trouve à l intérieur d un élément titre. Pour le savoir, vous allez doter votre classe MonContentHandler d une variable d instance privée de type booléen (nommez-la balisetitre) : elle vaudra true lorsque l analyseur sera à l intérieur d un élément titre, et false sinon. C est rapide à réaliser : cette variable doit être initialisée (dans le constructeur de MonContentHandler) à false, puis lorsque la méthode startelement() est invoquée et que son troisième argument vaut titre mettre balisetitre à true, et enfin lorsque la méthode endelement() est invoquée et que son troisième argument vaut titre mettre balisetitre à false. Programmez le contenu de startelement() et endelement() (aucune difficulté ; n oubliez pas le constructeur de MonContentHandler). Pour ce qui est de characters(), voici la solution : if (balisetitre) { String s = new String(arg0, arg1, arg2); 5
System.out.print(s); Si vous êtes en avance, essayez de ne sortir que les titres des recettes contenant du sel. 3.2 Et si le fichier XML n est pas bien formé? Pour savoir ce qui se passe dans ce cas-là, introduisez une erreur de syntaxe dans le fichier recettes.xml (introduisez-la au milieu du fichier) et observez. Normalement l analyse et la prise en compte des événements commence et lorsque l analyseur rencontre l erreur, il lance une exception Fatal Error et arrête tout. Cela vous montre que l analyse se fait bien à la volée. 3.3 Et si le fichier XML n est pas valide? Supprimez l erreur introduite au paragraphe précédent puis introduisez, au milieu du fichier recettes.xml, une balise non conforme à la DTD et observez. Normalement rien ne se passe. En effet par défaut, l analyseur ne vérifie pas la validité du document XML. Pour introduire cette validation il faut ajouter cette ligne au constructeur de MonLecteurSax : lecteurxml.setfeature("http ://xml.org/sax/features/validation", true) ; Là, le comportement est différent : la non-validité a bien été identifiée et localisée mais le traitement n a pas été interrompu. En fait l analyseur a lancé une exception Error simple (et pas une Fatal Error). Vous allez maintenant modifier ce comportement de façon à arrêter l analyse lorsqu une telle erreur est détectée. Pour cela vous devez créer une nouvelle classe MonErrorHandler qui implémente ErrorHandler. Pour associer MonErrorHandler à votre lecteur SAX, introduisez la ligne suivante dans le constructeur de MonLecteurSax : lecteurxml.seterrorhandler(new MonErrorHandler()) ; Sans changer la nouvelle classe MonErrorHandler, compilez et exécutez. Plus rien ne se passe : les erreurs de validation ne sont même plus annoncées. C est normal puisque vous avez surchargé par du code vide les méthodes qui sont invoquées lors de l apparition des exceptions Fatal Error, Error et Warning. Pour y remédier, dans chacune des 3 méthodes de la nouvelle classe, introduisez au moins throw arg0 ;. Compilez, exécutez, observez, et interprétez. Mettez de côté ces 3 classes (le code source uniquement), vous devrez les envoyer par mél à la fin du T.P. 4 Découverte du modèle DOM Temps souhaité : 30 minutes. 6
4.1 Construction d une arborescence DOM Sous eclipse, créez un nouveau projet Java, puis un package essaidom, et enfin une classe MonLecteurDom. Cette classe doit contenir une méthode main(). Dotez cette classe d une variable d instance privée lecteurdom de type DocumentBuilder. Dans cette classe, créez un constructeur sans argument, qui initialisera lecteurdom : DocumentBuilderFactory fabrique = DocumentBuilderFactory.newInstance(); lecteurdom = fabrique.newdocumentbuilder(); Comme pour SAX, créez une méthode analyse() avec comme argument une chaine de caractères qui contiendra l URI du fichier XML à analyser. Le corps de cette méthode contiendra juste l appel à la méthode parse() de la variable lecteurdom. La méthode main() peut maintenant ressembler à ça : try { MonLecteurDom monlecteurdom = new MonLecteurDom(); monlecteurdom.analyse("recettes.xml"); System.out.println("Document bien formé."); catch (SAXException e) { System.err.println("Erreur d analyse : " + e); catch (IOException e) { System.err.println("Erreur d entrée/sortie : " + e); catch (ParserConfigurationException e) { System.err.println("Erreur de configuration de l analyseur : " + e); Essayez différentes erreurs manifestes et observez : fichier XML manquant, DTD manquante, différentes erreurs de syntaxe XML, et enfin non respect de la DTD. Dans ce dernier cas, rien ne se passe car par défaut, comme pour SAX, la validation n est pas active. Pour l activer, il faut utiliser la méthode setvalidating() de la classe DocumentBuilderFactory. Dans notre cas ; fabrique.setvalidating(true) ; À partir de ce point, il est très facile d obtenir une arborescence DOM puisque la méthode parse() de la variable lecteurdom renvoie justement le document DOM analysé (de type org.w3c.dom.document). Faites les modifications nécessaires : à savoir faites en sorte que la méthode analyse() renvoie un org.w3c.dom.document, et affectez le retour de l appel de cette méthode analyse(), à une variable monarborescencedom de type org.w3c.dom.document. Vous obtenez ainsi une méthode main() qui ressemble à ça : Document monarborescencedom; try { MonLecteurDom monlecteurdom = new MonLecteurDom(); 7
monarborescencedom = monlecteurdom.analyse("recettes.xml"); System.out.println("Document bien formé."); catch (SAXException e) { System.err.println("Erreur d analyse : " + e); catch (IOException e) { System.err.println("Erreur d entrée/sortie : " + e); catch (ParserConfigurationException e) { System.err.println("Erreur de configuration de l analyseur : " + e); 4.2 Modification et sérialisation d une arborescence DOM Pour se familiariser avec le parcours d une arborescence DOM, vous allez commencer par essayer différents noeuds de l arborescence. Mais auparavant, désactivez la validation et changez l élément racine dans la déclaration DOCTYPE. En fin du bloc try de la méthode main(), insérez ceci : Node noeud = monarborescencedom; System.out.println("noeud = " + noeud.getnodename()); Le document est donc le noeud racine du document. Essayez maintenant son fils avec : Node noeud = monarborescencedom.getfirstchild() ; Le premier fils du document est donc la déclaration DOCTYPE. Pour avoir le 2ème fils : Node noeud = monarborescencedom.getfirstchild().getnextsibling() ; Le 2ème fils est donc l élément racine du document XML. Continuez ainsi pour vous familiariser avec cette arborescence. En guise d exercice, pour appliquer ces notions de navigation dans l arborescence DOM, vous allez changer le titre de la recette des Frites du document XML d origine. Pour cela il faut repérer le noeud correspondant à la balise racine (recettes), et fabriquer la liste de tous les fils de ce noeud : Node noeudrecettes = monarborescencedom.getfirstchild().getnextsibling(); NodeList listefils = noeudrecettes.getchildnodes(); Ensuite, il faut parcourir ces fils à la recherche de celui dont le titre contient la chaine de caractères Frites. En fait il faut examiner seulement un fils sur deux car ces fils sont alternativement du texte et une balise : for (int i = 0; i < listefils.getlength()/2 ; i++) { Node titre = listefils.item(2*i+1).getfirstchild().getnextsibling(); if (titre.getfirstchild().getnodevalue().contains("frites")) { // a completer 8
Pour changer la valeur du titre de la recette sélectionnée : titre.getfirstchild().setnodevalue("un truc trop gras") ; Ensuite il faut sérialiser la nouvelle arborescence DOM obtenue. Dans JAXP, le moyen le plus simple est d utiliser une transformation vide : TransformerFactory fabriqtransf = TransformerFactory.newInstance(); Transformer matransform = fabriqtransf.newtransformer(); Source entree = new DOMSource(monArborescenceDom); Result sortie = new StreamResult(new File("recettes2.xml")); matransform.transform(entree, sortie); Mettez de côté cette classe (le code source uniquement), vous devrez l envoyer par mél à la fin du T.P. 5 Utilisation de l API JDOM Temps souhaité : 1h30. La manipulation de JDOM est heureusement beaucoup plus facile. Avant de commencer, vous devez charger cette API. Vous pourriez aller la chercher sur http ://www.jdom.org. Mais elle est volumineuse car elle contient notamment la documentation et les codes sources. Dans le cadre de cet exercice, vous vous en passerez, et vous n allez charger que l archive jdom.jar qui se trouve ici : http ://www.emse.fr/ beaune/docnum/2006 2007/tp xml/. Créez un nouveau projet Java, puis un package essaijdom, et enfin une classe MonProjJdom. Cette classe doit contenir une méthode main(). Incluez la nouvelle API JDOM dans le classpath de ce projet : dans la fenêtre Package Explorer, avec le menu contextuel du projet, choisissez Build Path, puis Add External Archives..., et enfin choisissez jdom.jar là où vous l avez installé sur votre disque. Pour la suite du T.P., il existe un bon tutoriel ici : http ://cynober.developpez.com/tutoriel/java/xml/jdom/. Il est également disponible localement ici, en PDF : http ://www.emse.fr/ beaune/docnum/2006 2007/tp xml/tutorieljdom.pdf. Dans ce tutoriel, vous pouvez aller directement au chapitre 2 (page 6 de la version PDF) intitulé Créer un fichier XML avec JDOM. Vous n avez plus qu à suivre les indications de l auteur de ce tutoriel : faites ce premier exercice (classe JDOM1 pour créer un document XML et l afficher), puis l exercice suivant (classe JDOM2 pour créer un filtre), et enfin le dernier exercice du tutoriel : le paragraphe 4.3 (sautez les paragraphes 4.1 et 4.2) pour créer une transformation XSL-T. Vous appliquerez ce dernier programme sur le fichier XSL que vous avez fabriqué au T.P. précédent (paragraphe 2.3 sur HelloWorld). Mettez de côté ces 3 classes (le code source uniquement), vous allez devoir les envoyer par mél. 9
6 Rendu individuel de T.P. À 11h30 au plus tard vous devez envoyer un mél à Philippe.Beaune@emse.fr avec vos productions du paragraphe 3 (3 classes en attachement), du paragraphe 4 (1 classe en attachement), et du paragraphe 5 (3 classes en attachement). FIN 10