T.P. axe ISI

Transcription

T.P. axe ISI
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 à [email protected].
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. Dif2
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 ar3
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 à [email protected]
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