Tutoriel DOM et JDOM

51
Tutoriel DOM et JDOM Présenté par Cyril Vidal [email protected] Menu SI vous consultez ce document en ligne, vous pouvez cliquer sur n'importe que lien pour aller à la section correspondante 1. Introduction 2 2. L'API Document Object Model 3 3. L'API JDOM 39 Tutoriel DOM et JDOM Page 1

Transcript of Tutoriel DOM et JDOM

Page 1: Tutoriel DOM et JDOM

Tutoriel DOM et JDOM

Présenté par Cyril Vidal

[email protected]

MenuSI vous consultez ce document en ligne, vous pouvez cliquer sur n'importe que lien pour aller à la section correspondante

1. Introduction 2

2. L'API Document Object Model 3

3. L'API JDOM 39

Tutoriel DOM et JDOM Page 1

Page 2: Tutoriel DOM et JDOM

Section 1. Introduction

IntroductionCe tutoriel se donne pour objectif de donner un aperÇu des API DOM (DocumentObject Model) et JDOM , dont la finalité est de lire et de manipuler des fichiers XML.

Il présuppose un minimum de connaissances à propos du XML et surtout du langageJAVA, exlusivement utilisé ici. J'avoue ici humblement mon énorme dette vis-à-vis deBrett McLaughlin (co-inventeur de JDOM), dont la lecture attentive de l'excellentouvrage JAVA & XML, publié aux éditions O'Reilly, m'a fortement inspiré pour larédaction de ce petit tutoriel. Si un point vous paraît obscur voire même inexact, jeserais heureux d'en prendre connaissance par mail à l'adresse [email protected]

Bonne navigation!

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 2

Page 3: Tutoriel DOM et JDOM

Section 2. L'API Document Object Model

Introduction au DOMLes principales caractéristiques du modèle DOM sont les suivantes:

1. Le modèle DOM (contrairement sur ce point à une autre API fameuse: SAX),représente une spécification qui puise ses origines dans le consortium w3C(elle jouit donc sur ce point d'un niveau de 'respectabilité' égal à la spécificationXML elle-même).

2. Le modèle DOM est non seulement une spécification multi-plateformes, maisaussi multi-langages: il existe des liaisons avec Java, Javascript, CORBA etd'autres langages encore...

3. Le modèle DOM est organisé en niveaux plutôt qu'en versions.* DOM niveau 1 représente une Recommandation acceptée dont la

spécification complète est disponible à l'adressehttp://www.w3.org/TR/REC-DOM-Level-1 (sa traduction franÇaise estquant à elle disponible à l'adressehttp://xmlfr.org/w3c/TR/REC-DOM-Level-1/ ). Celle-ci détaille dans unepremière partie un ensemble minimum d'objets et d'interfacesfondamentales pour accéder et manipuler des objets documentaires (XMLmais aussi HTML), ainsi qu'un ensemble d'interfaces étendues spécifiques àla manipulation de documents XML (traitant par exemple les CDATA ou lesProcessing Instructions): cette première partie s'appelle le noyau DOM (coreDOM). Dans une seconde partie, la spécification DOM niveau 1 s'attache àdécrire les objets et les méthodes spécifiques aux documents HTML, quin'ont pas été définis dans le noyau.

* DOM niveau 2 , finalisé en novembre 2000, disponible à l'adressehttp://www.w3.org/TR/DOM-Level-2-Core/ étend le niveau 1 en proposantun certain nombre d'interfaces supplémentaires. En ce qui concerne letraitement de documents XML, DOM niveau 2 supporte en plus, parexemple, les espaces de noms (on peut ainsi créer ou retrouver un élémentou un attribut grâce non seulement à son nom local, mais aussi via sonespace de nom (ce sont par exemple et parmi de nombreuses autresméthodes les méthodes attachées à l'interface DocumentcreateElementNS(), createAttributeNS() etgetElementsByTagNameNS() qui permettent de faire cela).

* DOM niveau 3, finalisé le 22 octobre 2002, disponible à l'adressehttp://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20021022/DOM3-Core.html ,propose un certain nombre d'interfaces et de méthodes supplémentaires,parmi lesquelles la possibilité de retrouver les informations relatives à ladéclaration XML (version, encodage), la comparaison de noeuds ausein d'un document (via les méthodes isEqualNode() etisSameNode(), la comparaison de la position entre deux noeuds ausein d'un même document via la méthodecompareDocumentPosition(), et beaucoup d'autres choses encore...

Dans ce tutoriel, nous aborderons DOM Niveau 1 et 2, mais laisserons DOMNiveau 3 de côté, pour lui consacrer plus tard un tutoriel spécifique, lorque lesparseurs le supporteront.

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 3

Page 4: Tutoriel DOM et JDOM

Liaison avec les langagesLa spécification DOM laisse le choix du langage à utiliser pour implémenter le modèleDOM lui-même. Il faut donc développer des liaisons avec les langages afin dereprésenter la structure conceptuelle de DOM et l'utiliser avec tel langage déterminé.Ici, nous nous intéresserons à la liaison avec le langage Java. Celle-ci (versionsupportant DOM Level 2) peut-être librement téléchargée à l'adressehttp://www.w3.org/TR/DOM-Level-2/java-binding.html Cependant, si l'analyseur et leprocesseur XSLT que vous utilisez est assez récent, les paquetages DOM sontdésormais systématiquement inclus dans ces produits. Pour mieux comprendre lesrapports liant le modèle DOM de son implémentation concrète dans le lange JAVA,considérons en premier lieu la définition IDL de l'interface Document:interface Document :Node {readonly attribute DocumentType doctype;readonly attribute DOMImplementation implementation;readonly attribute Element documentElement;Element createElement(in DOMString tagName) provoque (DOMException);DocumentFragment createDocumentFragment();Text createTextNode(in DOMString data);Comment createComment(in DOMString data);CDATASection createCDATASection(in DOMString data) provoque (DOMException);ProcessingInstruction createProcessingInstruction(in DOMString target, in DOMString data) provoque (DOMException);Attr createAttribute(in DOMString Name) provoque (DOMException);entityReference createEntityReference(in DOMString name) provoque (DOMException);NodeList getElementsByTagName(in DOMString tagName); };

A fin de comparaison, l'implémentation Java d'une telle interface sera celle-ci:package org.w3c.dom;public interface Document extends Node {

public DocumentType getDoctype();public DOMImplementation getImplementation();public Element getDocumentElement();public Element createElement(String tagName)

throws DOMException;public DocumentFragment createDocumentFragment();public Text createTextNode(String data);public Comment createComment(String data);public CDATASection createCDATASection(String data)

throws DOMException;public ProcessingInstruction createProcessingInstruction(String target,

String data)throws DOMException;

public Attr createAttribute(String name)throws DOMException;

public EntityReference createEntityReference(String name)throws DOMException;

public NodeList getElementsByTagName(String tagname);public Node importNode(Node importedNode,

boolean deep)throws DOMException;

public Element createElementNS(String namespaceURI,String qualifiedName)throws DOMException;

public Attr createAttributeNS(String namespaceURI,String qualifiedName)

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 4

Page 5: Tutoriel DOM et JDOM

throws DOMException;public NodeList getElementsByTagNameNS(String namespaceURI,

String localName);public Element getElementById(String elementId);

}

Comment DOM travailleComme il a déjà été dit précédemment, à l'aide du Modèle Objet de Document, lesprogrammeurs peuvent construire des documents, naviguer dans leur structure, etajouter, modifier, ou supprimer soit des éléments soit du contenu.

Par chance, cette API ressemble très étroitement à la structure des documents qu'ellemodélise. Par exemple, si l'on considère le document XML suivant:<?xml version="1.0" encoding="iso-8859-1?"><catalogue><livre><titre>La généalogie de la morale</titre><auteur>Friedrich Nietzsche</auteur><édition>folio essais</édition><ISBN>2-07-032327-7</ISBN>

</livre><livre>

<titre>Réflexions sur la poésie</titre><auteur>Paul Claudel</auteur><édition>folio essais</édition><ISBN>2-07-032746-9</ISBN>

</livre></catalogue>

DOM le représente ainsi:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 5

Page 6: Tutoriel DOM et JDOM

DOM présente les documents sous forme d'une hiérarchie d'objetsorg.w3C.dom.Node, à partir desquels d'autres interfaces plus spécialisées sontelles-mêmes implémentées: Document, Element, Attr, Text,... Grâce à ce modèle,on peut traiter tous les composants DOM soit par leur type générique, Node, soit parleur type spécifique (Element, Attr): de nombreuses méthodes de navigation, parexemple getChildNodes() ou getLastChild() sont disponibles dans l'interfaceNode de base, et permettent ainsi une navigation dans l'arborescence sans avoir às'inquiéter du type spécifique de composant traité.

Récupération de noeudsAprès cette brève présentation, utilisons DOM afin de parser et de récupérer desdonnées de notre fichier catalogue.xml.La première chose à faire de créer un objet de type org.w3c.dom.Document. Tantque l'intégralité du document n'a pas été analysée et ajoutée dans la structurearborescente à ce niveau supérieur par rapport à l'élément racine du document XML,les données du fichier d'entrée ne se trouvent pas dans un état utilisable. En fait,comme le standard DOM ne spécifie pas de méthode pour obtenir l'objet Document, ilexiste plusieurs méthodes à cette fin. Puisque nous nous focalisons sur le parseurXerces, la méthodologie à suivre est la suivante:

1. D'abord, ne pas oublier d'importer le parseur concerné, ici Xerces, vial'instruction import org.apache.xerces.parsers.DOMParser

2. Ensuite, instancier un objet de la classe Parseur, via DOMParser parseur =new DOMParser();

3. Enfin, utiliser la méthode getDocument() du parseur ainsi obtenu afin d'obtenir

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 6

Page 7: Tutoriel DOM et JDOM

l'objet Document issu de l'analyse XML.

Voici le code correspondant://Importation de l'analyseur xercesimport org.apache.xerces.parsers.DOMParser;public class TestDOM{

public static void main( String [] args ) throws Exception{

DOMParser parser = new DOMParser();parser.parse("catalogue.xml");Document document = parser.getDocument();

}}

Nous verrons plus loin dans ce tutoriel qu'il existe une autre faÇon de procéder vial'API JAXP afin de standardiser l'accès à une arborescence DOM depuis uneimplémentation quelconque d'un analyseur.

Tentons à présent de récupérer les titres des livres composant le catalogue: Pour cefaire, nous récupérons d'abord l'élément racine du document catalogue.xml, via laméthode getDocumentElement() appliquée au noeud de type Document documentprécédemment défini. Ensuite, nous définissons un noeud de type NodeList,équivalent à une Collection Java, qui regroupe tous les éléments dont le type est titre,via la méthode getElementsByTagName("titre"). Enfin, nous itérons sur cetteNodeList afin de récupérer la valeur de chacun des noeuds de type Text, fils deséléments <titre>: en effet la structure hiérarchique du DOM impose de devoirrécupérer le contenu textuel d'un élément, non à partir de l'élément lui-même(comme cela se fait avec JDOM ainsi que nous le verrons plus tard grâce à laméthode getText()), mais à partir du noeud Text fils de l'élément. La premièreétape (récupération du noeud Text fils de l'élément <titre> se fait grâce à laméthode getFirstChild(), tandis que la récupération de la valeur textuelle se faiten utilisant la méthode getNodeValue() (cette denière méthode s'appliqueégalement aux noeuds CDATA, comment et processing instructions).

Voici le code correspondant auxx étapes que l'on vient de décrire://DOMimport org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NodeList;...Element catalogue = document.getDocumentElement();

NodeList titres = catalogue.getElementsByTagName("titre");System.out.println("Les titres des livres du catalogue sont: ");for (int i=0; i<titres.getLength(); i++) {System.out.println(titres.item(i).getFirstChild().getNodeValue());

Lorsque nous exécutons en ligne de commande ce fichier (dont la source estdisponible ici), nous obtenons l'écran suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 7

Page 8: Tutoriel DOM et JDOM

Si nous voulons récupérer l'ensemble des informations afférentes à chacun des livres,on risque de se livrer à un travail assez fastidieux. DOM est en effet un langagebavard. Peut-être est-il plus judicieux de créer des méthodes réutilisables pourchacune des quatre éléments caractérisant le livre (auteur, titre, edition, ISBN).

Récupération de noeuds (suite)Nous allons donc créer dans fichier annexe, que nous appelerons AnnexeDOM.java,différentes méthodes permettant de systématiser notre recherche d'éléments dans ledocument xml à notre disposition.

On commence pour ce faire par définir une première méthode génériquetrouveTexte(), prenant comme arguments à la fois un Element et une chaîne decaractères String, et qui accomplit les deux actions suivantes:

1. D'abord, récupère l'élément de nom indiqué par le paramètre via la méthodetrouvePremierElement().

2. Ensuite, récupère le contenu textuel d'un tel élément via la méthode de mêmenom que la méthode appelante, i.e. trouveTexte(), mais surchargée avec unseul paramètre. (On rappelle que Java permet de définir dans une classeplusieurs méthodes portant le même nom, tant que chaque méthode possède unensemble unique de paramètres. C'est ce que l'on appelle la surcharge deméthode)

public static String trouveTexte( Element element, String nom ){

Element elementNom = trouvePremierElement( element, nom );return trouveTexte(elementNom );

}

La méthode trouvePremierElement est très simple: A partir d'une liste de noeudsprécédemment définie (en l'occurence, la liste des éléments livre contenus dansl'élément catalogue), on définit une autre NodeList, comportant tous les noeudsayant le nom passé en argument. Si il n'y a aucun élément répondant à ce nom, unmessage d'erreur est renvoyé. Sinon, le premier élement de la liste est renvoyé enutilisant la méthode item() de la classe NodeList et en lui passant la valeur 0comme argument (les index commencent tous par zéro). Le code de cette méthode estdonc le suivant:public static Element trouvePremierElement( Element element, String nom ) {

NodeList nl = element.getElementsByTagName( nom );if ( nl.getLength() < 1 )

throw new NullPointerException(

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 8

Page 9: Tutoriel DOM et JDOM

"Element: "+element+" ne contient pas: "+nom);return (Element)nl.item(0);

}

Enfin, la troisième et dernière méthode dont nous avons besoin,trouveTexte(Element element) récupère la valeur textuelle de l'élémentpassé en paramètre. Exactement de la même faÇon que vue dans le panneauprécédent, en descendant dans la hiérachie au niveau du noeud Text et en enrécupérant la valeur via la méthode getNodeValue()public static String trouveTexte( Element element){

return element.getFirstChild().getNodeValue();}

Le fichier source AnnexeDOM.java est disponible ici

Dès lors, la récupération de toutes les informations concernant chacun de ces livresest très simplifiée: Après avoir repris les quelques étapes du panneau précédent(instanciation d'un parseur, création du noeud Document, récupération de l'élémentracine du document, récupération de tous les éléments <titre>), on appelle pourchaque caractéristique du livre définie dans catalogue.xml, la méthode de classetrouveTexte() définie dans la classe AnnexeDOM, avec comme arguments unnoeud <livre> extrait de la NodeList sur laquelle on itère, et la chaîne de caractèrescorrespondant au nom de l'élément enfant de ce noeud <livre>que l'on veutrécupérer. Le code est le suivant://DOMimport org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NodeList;//Importation de l'analyseur xercesimport org.apache.xerces.parsers.DOMParser;public class TestDOM1{

public static void main( String [] args ) throws Exception{

DOMParser parser = new DOMParser(); //instanciation parseurparser.parse("catalogue.xml"); //analyse du fichier catalogue.xmlDocument document = parser.getDocument(); //récupération du document englobant toutes les

données analyséesElement catalogue = document.getDocumentElement();

//récupération de l'élément racine du document, ici <catalogue>NodeList livres = catalogue.getElementsByTagName("livre");

//récupération de tous les éléments <livre> inclus dans <catalogue>System.out.println("Les caractéristiques des livres du catalogue sont:");

for( int i=0; i<livres.getLength(); i++ ) {String titre = AnnexeDOM.trouveTexte( (Element)livres.item(i),"titre" );String auteur = AnnexeDOM.trouveTexte( (Element)livres.item(i),"auteur" );

String édition = AnnexeDOM.trouveTexte((Element)livres.item(i),"édition" );

String ISBN = AnnexeDOM.trouveTexte( (Element)livres.item(i),"ISBN" );System.out.println("\ntitre: "+ titre+"\nauteur:

"+auteur+"\nédition: "+ édition +"\nISBN: "+ ISBN);}

}}

Lorqu'on exécute ce programme (dont le fichier source est disponible ici), on obtient

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 9

Page 10: Tutoriel DOM et JDOM

cran suivant:

Test du type de noeudsOn peut améliorer certains points du code précédent. Par exemple, lorsqu'on veutrécupérer la valeur textuelle d'un noeud, on a vu que l'on utilisait la méthodegetFirstChild().getNodeValue(). Mais que se passe-t-il si par hasard ondevait modifier le document catalogue.xml pour ajouter un élément<commentaire> donnant quelques précisions sur l'auteur de tel ou tel livre, commececi:<livre>

<titre>Réflexions sur la poésie</titre><auteur><commentaires>Paul Claudel n'est pas seulement grand

écrivain, mais aussi grand critique</commentaires>PaulClaudel</auteur>

<édition>folio essais</édition><ISBN>2-07-032746-9</ISBN>

</livre>

Le code source correspondant au fichier catalogue1.xml est disponible ici.

En lanÇant TestDOM1 sur catalogue1.xml, on obtient alors le résultat suivant:

Bien évidemment, au niveau du livre Réfléxions sur la poésie, nous obtenons uneréférence null pour son auteur. Simplement parce que le premier élément fils n'estpas de type Text mais Element, ce pourquoi nous ne pouvons lui appliquer laméthode getNodeValue().

Afin de résoudre cette difficulté, il nous faut donc changer la méthodetrouveText(Element element) de la classe AnnexeDOM, afin de tester le type

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 10

Page 11: Tutoriel DOM et JDOM

de noeud concerné. Il existe pour cela deux méthodes:1. Mot-clé java: instance of On commence par créer une NodeList qui stocke

tous les noeuds fils de l'élément passé en argument. Nous testons ensuitechacun de ces noeuds, afin de savoir si son type est Text ou non, via l'instructionif ( numéro instanceof Text ). Si c'est le cas, nous l'ajoutons au buffer.Enfin, nous créons un objet String à partir de l'objet StringBuffer en utilisantla méthode toSring() afin que le résultat puisse être affiché sur l'écran.StringBuffer buffer = new StringBuffer();

NodeList fils = element.getChildNodes();for(int i=0; i<fils.getLength(); i++) {

Node numéro = fils.item(i);if ( numéro instanceof Text )

buffer.append( numéro.getNodeValue() );}return buffer.toString();

Le fichier source du fichier AnnexeDOMbuf.java est disponible ici

2. Méthode de l'interface Node: getNodeType() L'interface Node définit pouraccomplir la même tâche une méthode utilitaire, getNodeType(), qui retourneune valeur entière. On peut comparer cette valeur avec un ensemble deconstantes également définies dans l'interface Node , qui sont, entre autres:Node.DOCUMENT_NODE, Node.ELEMENT_NODE, Node.TEXT_NODE,Node.CDATA_SECTION_NODE, Node.COMMENT_NODE,Node.PROCESSING_INSTRUCTION_NODE, Node.DOCUMENT_TYPE_NODE. Ici,puisque nous voulons tester si le noeud est de type Text, nous utilisons doncl'instruction if (numéro.getNodeType() == Node.TEXT_NODE), ce qui donne:StringBuffer buffer = new StringBuffer();

NodeList fils = element.getChildNodes();for(int i=0; i<fils.getLength(); i++) {

Node numéro = fils.item(i);if ( numéro.getNodeType() == Node.TEXT_NODE )

buffer.append( numéro.getNodeValue() );}return buffer.toString();

Récupération d'attributsVoyons à présent comment récupérer un genre particuliers de noeuds quereprésentent les attributs. Supposons que dans notre fichier catalogue.xml, nousrajoutions des attributs, spécifiant par exemple la langue utilisée:<catalogue><livre><titre langue="fr">La généalogie de la morale</titre>...

</livre><livre>

<titre langue="fr">Réflexions sur la poésie</titre>...

</livre></catalogue>

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 11

Page 12: Tutoriel DOM et JDOM

et que nous voulions les récupérer afin d'afficher le nom et la valeur de ces attributs.

Il nous faut rajouter une méthode trouveAttribut() dans notre fichierAnnexeDOM.java, qui puisse faire cela:public static void trouveAttribut( Element element, String nom )

{Element elementNom = trouvePremierElement( element, nom );NamedNodeMap attributs = elementNom.getAttributes();for(int i=0; i<attributs.getLength(); i++) {

Node numéro = attributs.item(i);String nomAttribut = numéro.getNodeName();String valeurAttribut = numéro.getNodeValue();System.out.print(nomAttribut + " =\"" + valeurAttribut + "\"");}

}

Le fonctionnement d'une telle méthode est le suivant: En premier lieu, nous réutilisonsla méthode trouvePremierElement déjà définie dans la classe afin de récupérerchaque premier élément d'un nom donné au sein d'un élément donné (ici, comme onl'a vu, il s'agit d'un élément <livre>) Ensuite, nous récupérons les attributs de cetélément grâce à la méthode getAttributes (proposée par l'interface Node) ,laquelle retourne un objet NamedNodeMap. Cet objet partage avec NodeList lapropriété d'être itérable. C'est donc ce que nous faisons, en récupérant à chaque foisà la fois le nom de l'attribut via getNodeName() ainsi que sa valeur, viagetNodeValue()

Pour appeler cette méthode à partir de la classe TestDOM, il suffit alors d'insérer entredeux instructions d'impression écran, l'appel à notre méthode trouveAttribut(),comme ceci:System.out.println("Les caractéristiques des livres du catalogue sont:");

for( int i=0; i<livres.getLength(); i++ ) {String titre = AnnexeDOMbuf1.trouveTexte( (Element)livres.item(i),"titre" );String auteur = AnnexeDOMbuf1.trouveTexte( (Element)livres.item(i),"auteur" );

String édition = AnnexeDOMbuf1.trouveTexte((Element)livres.item(i),"édition" );

String ISBN = AnnexeDOMbuf1.trouveTexte( (Element)livres.item(i),"ISBN" );System.out.println("\ntitre: "+ titre);AnnexeDOMbuf1.trouveAttribut((Element)livres.item(i),"titre");System.out.print("\nauteur: " + auteur + "\nédition: " + édition + "\nISBN: "+ ISBN);

En exécutant la classe ainsi définie, nous obtenons l'écran suivant:

Les fichiers sources sont: catalogue2.xml, TestDOM2.java, et enfinAnnexeDOMbuf1.java.

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 12

Page 13: Tutoriel DOM et JDOM

Création et Modification d'une arboresence DOM (viaservlet)Nous allons voir dans cet exemple un petit peu plus sophistiqué (car utilisant uneservlet) la faÇon de modifier une arborescence DOM. Le but est le suivant: on proposeun formulaire à l'utilisateur dans lequel celui-ci peut indiquer le n°, le titre, l'auteur, ainsiqu'une courte description d'un livre. A partir de là, de deux choses l'une: soit il existedéjà un livre de même numéro et alors il faut mettre à jour le détail du livre, soit le livren'existe pas, et il faut alors créer le fichier XML correspondant. Le code HTML duformulaire de départ pourrait être le suivant:<html><head><title>Saisie/Mise à jour d'un livre</title></head><body><h1 align='center'>Saisie/Mise à jour d'un livre</h1><p align='center'><form method='POST' action='/tuto/update'>Numéro du livre: <br/><input name='no' type='text' maxLength='10' /><br/><br/>Titre du livre: <br /><input name='titre' type='text' maxLength='50' /><br /><br />Auteur du livre: <br /><input name='auteur' type='text' maxLength='50' /><br /><br />Description du livre: <br /><textarea name='description' rows='10' cols='30' wrap='wrap'

></textarea><br /><br /><input type='reset' value='Recommencer' />  <input type='submit' value='Ajouter/Mettre à jour' /></form></p></body></html>

On remarque que la cible de ce formulaire est comme dit précédemment une servlet,et que le fait d'appuyer sur le bouton de valeur Ajouter/Mettre à jour lancera laméthode doPost() de la servlet située à l'URLhttp://localhost:8080/tuto/update. Nous utilisons ici le moteur de servletTomcat4.0.1 dont nous avons déjà décrit l'installation dans un précédent tutoriel, àl'adresse http://www.planetexml.com/base_xml/base_xml-4-3.html . La seule chose àpréciser ici est la manière d'accéder à la servlet. Nous avons créé l'arborescencewebapps->tuto->WEB-INF->classes ->MiseAJourServlet.class, ainsi que lemontre la fenêtre d'Explorateur Windows suivante:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 13

Page 14: Tutoriel DOM et JDOM

Ceci étant mis en place, il suffit juste d'éditer un fichier web.xml dans le dossierWEB-INF et d'y insérer les quelques lignes de code suivantes:<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app><servlet><servlet-name>UpDate</servlet-name><servlet-class>MiseAJourServlet</servlet-class>

</servlet><servlet-mapping><servlet-name>UpDate</servlet-name><url-pattern>/update</url-pattern>

</servlet-mapping></web-app>

Le premier élément <servlet> associe un nom, ici UpDate à la classe de servletMiseAJourServlet, tandis que le deuxième élément <servlet-mapping> faitcorrespondre un tel nom UpDate à une URL, ici /update, valable à partir du contextede la servlet, ici tuto (le sous-dossier contenant une telle servlet et directement enfantdu dossier webapps). Notre servlet sera donc ainsi accessible en pointant sur l'URLhttp://localhost:8080/tuto/update.

De plus, si l'on veut éviter de redémarrer Tomcat à chaque fois que l'on modifieune classe dans notre répertoire de travail, il convient également de modifier lefichier server.xml situé dans le répertoire conf de Tomcat de la faÇon suivante:<!--Contexte Repertoire tuto -->

<Context path="/tuto" docBase="tuto" debug="0"reloadable="true"/>

L'attribut reloadable="true" permet de pouvoir reloader les classesautomatiquement dès que des changements sont détectés. Si l'on voulait changerl'adresse URL de notre servlet, par exemple en

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 14

Page 15: Tutoriel DOM et JDOM

/update, il suffirait de changer l'attribut path de la faÇon suivante:path="/cyril"

. Regardons à présent à quoi pourrait ressembler notre méthode doPost()://importations nécessairesimport java.io.File;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.xml.sax.SAXException;// DOMimport org.w3c.dom.Attr;import org.w3c.dom.Document;import org.w3c.dom.DOMImplementation;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.w3c.dom.Text;// parseurimport org.apache.xerces.dom.DOMImplementationImpl;import org.apache.xerces.parsers.DOMParser;//Recherche le fichier nommé et soit le crée, soit le remplace

public void doPost(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException {// Récupère les valeurs de paramètresString no = req.getParameterValues("no")[0];String titre = req.getParameterValues("titre")[0];String auteur = req.getParameterValues("auteur")[0];String description = req.getParameterValues("description")[0];// Contrôle si le fichier existeDocument doc = null;File fichierXML = new File(REPERTOIRE + "livre-" + no + ".xml");if (!fichierXML.exists()) {

// Creer une nouvelle arborescence DOMDOMImplementation domImpl = new DOMImplementationImpl();doc = domImpl.createDocument(null, "livre", null);Element root = doc.getDocumentElement();// no du livre comme attributroot.setAttribute("no", no);

//Titre du livreElement elementTitre = doc.createElement("titre");

Text texteTitre = doc.createTextNode(titre);elementTitre.appendChild(texteTitre);root.appendChild(elementTitre);// Auteur du livreElement elementAuteur = doc.createElement("auteur");Text texteAuteur = doc.createTextNode(auteur);elementAuteur.appendChild(texteAuteur);root.appendChild(elementAuteur);// Description du livreElement elementDescription = doc.createElement("description");Text texteDescription = doc.createTextNode(description);elementDescription.appendChild(texteDescription);root.appendChild(elementDescription);

La méthode ci-dessus exposée commmence par récupérer les valeurs des quatreparamètres no, titre, auteur et description. Une fois cela fait, elle crée unnouvel arbre DOM via la méthode createDocument() (introduite dans DOM

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 15

Page 16: Tutoriel DOM et JDOM

Niveau 2), appliquée à la classe DOMImplementation, classe de base de tout travailde création de DOM. Ici, nous utilisons l'implémentation de xerces,org.apache.xerces.dom.DOMImplementationImpl, aucune manière neutre enterme de vendeur n'étant pour l'instant disponible, même si DOM Niveau 3 et l'APIJAXP lancent quelques pistes en ce sens. La méthode createDocument prendcomme premier argument l'espace de nommage de l'élément racine du document(pour l'instant, nous n'en utilisons aucun et nous indiquons donc la valeur null). Lesecond argument de cette seconde méthode représente le nom de l'élément racinelui-même (ici livre) et le troisième élément représente une instance de DocTypeassociée à ce document (ici, nous n'en utilisons aucune, et nous spécifions doncencore la valeur null). Une fois l'arbre DOM crée, nous récupérons l'élément racinevia la méthode getDocumentElement(), auquel nous ajoutons dans un premiertemps un attribut no via setAttribute("no",no), puis dans un deuxième tempsles éléments titre, auteur et description. Dans ce dernier cas, la méthode esttoujours la même: on commence par créer l'élément lui-même via la méthodecreateElement( String nomElement), puis le contenu textuel de cet élémentvia createTextNode(String texte), que nous finissons par attacher à l'élémentracine via la méthode appendChild(Element parent)

Sinon, si le fichier spécifié existe déjà, il nous faut alors le modifier à l'aide desnouveaux paramètres renseignés par l' utilisateur. Le code afin de parvenir à un tel butpourrait être le suivant:DOMParser parser = new DOMParser();

parser.parse(fichierXML.toURL().toString());doc = parser.getDocument();Element root = doc.getDocumentElement();

// Titre du livreNodeList elementsTitre = root.getElementsByTagName("titre");Element elementTitre = (Element)elementsTitre.item(0);Text texteTitre = (Text)elementTitre.getFirstChild();texteTitre.setData(titre);// Auteur du livreNodeList elementsAuteur = root.getElementsByTagName("auteur");Element elementAuteur = (Element)elementsAuteur.item(0);Text texteAuteur = (Text)elementAuteur.getFirstChild();texteAuteur.setData(auteur);// Description du livreNodeList elementsDescription =

root.getElementsByTagName("description");Element elementDescription = (Element)elementsDescription.item(0);// Supprime et recrée la description

root.removeChild(elementDescription);elementDescription = doc.createElement("description");

Text texteDescription = doc.createTextNode(description);elementDescription.appendChild(texteDescription);

root.appendChild(elementDescription);

On commence par parser le document existant via la méthode parse déjà vue, puison récupère son élément racine, toujous avec la méthode getDocumentElement().A partir de là, on récupère chacun des premiers noeuds de nom <auteur> ou<titre>, puis on leur transfère un contenu contextuel correspondant au paramètrede même nom via la méthode setData(). Pour l'élément <description>, on utiliseune approche légèrement différente car un tel élément peut contenir de nombreuxéléments imbriqués (par exemple des élément HTML) qui empêchent la récupérationdu premier élément textuel à remplacer par la valeur du paramètre description. Leplus simple ici est d'enlever directement l'élement <description> de la hiérarchie

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 16

Page 17: Tutoriel DOM et JDOM

via la méthode removeChild() et de le remplacer par un nouveau auquel on affectela valeur textuelle égale à la valeur du paramètre.

Enfin, on sérialise l'arbre DOM (via la classe DOMSerialiseur.java que nous détaillonsau prochain chapitre), puis on écrit un message de bonne exécution de la requête viale code suivant:// Serialise l'arborescence DOM

DOMSerialiseur serializer = new DOMSerialiseur();serializer.serialise(doc, fichierXML);// Confirmation écrite du traitementPrintWriter out = res.getWriter();res.setContentType("text/html");out.println("Merci pour votre requête: " +

"Celle-ci a bien été traitée.");out.close();

Tant qu'à faire, onpeut également mettre le code HTML du formulaire au sein de laservlet elle-même, par exemple à travers la méthode doGet(). Code source completde la servlet MiseAJourServlet.java Lorqu'on pointe le navigateur sur l'adressehttp://localhost:8080/tuto/update, on obtient alors l'écran suivant:

Si l'on renseigne le formulaire de la manière suivante:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 17

Page 18: Tutoriel DOM et JDOM

l'on obtient l'écran suivant après avoir cliqué sur le bouton de confirmation de requêtes,si tout se passe bien:

et l'on peut vérifier la présence du fichier livre-1.xml dans le dossierC:\DOM\tuto, qui sera le suivant:

Si l'on remplit à nouveau le formulaire de la faÇon suivante:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 18

Page 19: Tutoriel DOM et JDOM

on vérifie que l'on obtient à présent le fichier livre-1.xml suivant:

SérialisationJusqu'à présent, nous avons parsé le document catalogue.xml ou variantes, enconstruisant grâce à Xerces une arborescence DOM de ce document. A partir de là,nous avons récupéré tel ou tel élément, ou bien encore tel ou tel attribut de cet arbreDOM. Cependant, l'une des quesiotns les plus courantes concernant l'utilisation deDOM concerne la sérialisation des arborescences DOM, autrement dit, la faÇon dontcelles-ci peuvent être enregistrées dans un fichier. En fait, les Niveaux 1 et 2 deDOM ne proposent aucune manière de faire cela, et cela reviendra à la charge deDOM Niveau 3. En attendant, et avant de voir une autre faÇon de contourner leproblème via JAXP1.1 et l'API TrAX, voyons comment procéder à une telle

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 19

Page 20: Tutoriel DOM et JDOM

rialisation via des moyens ordinaires (et un peu lourds, il faut le reconnaître...). Etantdonné que nous avons plusieurs versions du fichier catalogue.xml, une bonne idéeconsiste peut-être à indiquer le nom du fichier à sérialiser en paramètre, afin de ne pasavoir à changer celui-ci dans le code et de recompiler à chaque fois. Pour cela, nousécrivons le code suivant:import java.io.File;import org.w3c.dom.Document;import org.apache.xerces.parsers.DOMParser;public class Serialiseur {public void serialise(String documentXML, String nomFichierSortie)throws Exception {File fichierSortie = new File(nomFichierSortie);DOMParser parseur = new DOMParser();parseur.parse(documentXML);Document document = parseur.getDocument();

//Sérialise}public static void main(String[] args) {if (args.length !=2) {

System.out.println("Usage: java Serialiseur [document XML à lire] "+ "[nom du fichier de sortie]");

System.exit(0);}try {

Serialiseur serialiseur = new Serialiseur();serialiseur.serialise(args[0], args[1]);

} catch (Exception e) {e.printStackTrace();}}}

Il n'y a rien de particulier à dire jusqu'ici: nous définissons la classe Serialiseuravec une méthode serialise() qui prend comme arguments respectivement ledocument XML à sérialiser et le fichier de sortie. Si un nombre différent d'argumentsest passé, on obtient alors un message d'information sur la procédure à suivre. Laméthode principale utilise elle-même la méthode printStackTrace() de l'objetException, qui fournit un message et la trace de la pile au flux de sortie d'erreurstandard, ici l'écran.

Sérialisation (suite 1)Dans le code défini dans le panneau précédent, il nous manque bien sûr l'essentiel,c'est-à-dire la définition d'une classe effective de sérialisation. Nous allons icicommenter pas à pas une classe de sérialisation codée par Brett McLaughlin, un desgourous JAVA/XML, et qu'il propose dans le chapitre consacré à DOM dans son livreJAVA&XML publié aux éditions O'REILLY. Cette classe de sérialisation resteimparfaite, notamment en termes d'indentations et de retours chariots, mais elle donneun très bon aperÇu de la faÇon dont on peut travailler en DOM. Dans un premiertemps, nous configurons notre classe DOMSerialiseur de manière à la rendre laplus générique possible. Cette configuration concerne:

1. la mise en forme du flot de sortie d'une part, via la définition de deux variablesd'instance privées indentation et sautLigne, ainsi que la définition de deux

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 20

Page 21: Tutoriel DOM et JDOM

accesseurs en modification de ces deux variables, respectivementsetIndentation() et setSautLigne(). (On rappelle que par convention, lenom de l'accesseur, qu'il soit en modification ou en consultation, doit être préfixérespectivement par le mot set ou get, suivi du nom de la variable qu'il modifie enpassant la première lettre de la variable en majuscule mais en conservant lacasse pour le reste des lettres). D'autre part, pour éviter toute ambiguîté, lorque lenom du paramètre d'une méthode est identique au nom d'un membre de donnéesde la classe, on doit utiliser le nom this pour faire référence au membre dedonnées de la classe.

2. Ensuite, nous nous occupons de considérer les principaux formats de sortie duflot XML, lesquels concernent principalement le format File ainsi queOutputStream, et qui peuvent tous deux se ramener grâce aux classes JavaE/S java.io.OutputStreamWriter et java.io.FileWriter à une mêmeclasse de base: la classe abstraite java.io.Writer.

import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.Writer;import org.w3c.dom.Document;import org.w3c.dom.DocumentType;import org.w3c.dom.NamedNodeMap;import org.w3c.dom.Node;import org.w3c.dom.NodeList;public class DOMSerialiseur {

// Indentation à utiliserprivate String indentation;// Saut de ligne à utiliserprivate String sautLigne;// Constructeur initialisant les membres de donnéespublic DOMSerialiseur() {

indentation = "";sautLigne = "\n";

}// Accesseur en modification définissant l'indentation à utiliserpublic void setIndentation(String indentation) {

this.indentation = indentation;}// Accesseur en modification définissant le saut de ligne à utiliserpublic void setSautLigne(String sautLigne) {

this.sautLigne = sautLigne;}//Sérialisation de l'arborescence DOM en l'OutputStream de sortie

mentionnépublic void serialise(Document doc, OutputStream out)

throws IOException {Writer writer = new OutputStreamWriter(out);serialise(doc, writer);

}//Sérialisation de l'arborescence DOM vers le fichier de sortie

mentionnépublic void serialise(Document doc, File fichier)

throws IOException {Writer writer = new FileWriter(fichier);serialise(doc, writer);

}//Sérialisation de l'arborescence DOM vers le Writer mentionnépublic void serialise(Document doc, Writer writer)

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 21

Page 22: Tutoriel DOM et JDOM

throws IOException {// Exécution de la sérialisation via la méthode

serialiseNoeud() définie dansle panneau suivant.

//On s'assure finalement que tout le flot soit vidéwriter.flush();

}

Sérialisation (suite 2)Venons en enfin à notre méthode de sérialisation, serialiseNoeud(). Un desgrands avantages du Modèle Objet Document consiste en ce que toutes les structuresqu'il définit étendent l'interface Node, et donc qu'une seule et même méthode, àcondition de différencier chacune de ces structures dans ce qu'elle a de spécifique (parexemple via une commande switch) permet de définir et de gérer le processusconsistant à traverser l'arborescence DOM.

On commence ainsi par tester via la méthode getNodeType() le cas d'un noeud detype Document: si la condition est remplie, on commence par écrire la déclarationXML (non prise en charge par DOM Niveau 2: DOM Niveau 3 devrait remédier àcela), puis on saute une ou plusieurs lignes suivant la faÇon dont on a défini notrevariable d'instance sautLigne, et enfin on boucle sur chacun des noeuds fils deDocument, récupérés par l'intermédiaire de la méthode getChildNodes(), enrappelant de manière récursive la même méthode serialiseNoeud().public void serialiseNoeud(Node noeud, Writer writer,

String niveauIndentation)throws IOException {// Determine l'action à accomplir en fonction du type de noeudswitch (noeud.getNodeType()) {

case Node.DOCUMENT_NODE:writer.write("<?xml version='1.0'

encoding='iso-8859-1'?>");writer.write(sautLigne);// boucle sur chaque enfantNodeList noeuds = noeud.getChildNodes();if (noeuds != null) {

for (int i=0; i<noeuds.getLength(); i++) {serialiseNoeud(noeuds.item(i), writer, "");

}}break;

...}

}

On remarque ici que les éléments fils du document racine (dans notre cas, il s'agit del'unique élément <catalogue> seront écrits en sortie juste en dessous de la déclarationXML, sans indentation par rapport à celle-ci. Si l'on veut modifier cela, il suffitsimplement de rajouter des espaces blancs au sein des guillemets dans l'instructionserialiseNoeud(noeuds.item(i), writer, "");. On définit ainsi uneindentation par défaut à laquelle on ajoute à chaque niveau hiérarchique del'arborescence l'indentation définie par la variable d'instance SautLigne. Sanssurprise, l'action à accomplir relativement à un Element consiste à afficher son nom,

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 22

Page 23: Tutoriel DOM et JDOM

ses attributs, sa valeur, puis à s'occuper de ses fils. Cela se fait sans problèmes viarespectivement les méthodes getNodeName(), getAttributes() et en rappelantde manière récursive toujours la méthode serialiseNoeud() pour le reste. Ce quidonne le code suivant, à mettre à la suite du précédent:case Node.ELEMENT_NODE:

String nom = noeud.getNodeName();writer.write(niveauIndentation + "<" + nom);NamedNodeMap attributs = noeud.getAttributes();for (int i=0; i<attributs.getLength(); i++) {

Node courant = attributs.item(i);writer.write(" " + courant.getNodeName() +

"=\"" + courant.getNodeValue() +"\"");

}writer.write(">");// boucle sur chaque enfantNodeList enfants = noeud.getChildNodes();if (enfants != null) {

if ((enfants.item(0) != null) &&(enfants.item(0).getNodeType() ==Node.ELEMENT_NODE)) {writer.write(sautLigne);

}for (int i=0; i<enfants.getLength(); i++) {

serialiseNoeud(enfants.item(i), writer,niveauIndentation + indentation);

}if ((enfants.item(0) != null) &&

(enfants.item(enfants.getLength()-1).getNodeType() ==

Node.ELEMENT_NODE)) {writer.write(niveauIndentation);

}}writer.write("</" + nom + ">");writer.write(sautLigne);break;

Il est à remarquer qu'on teste en début et en fin de boucle sur les enfants le type dupremier et du dernier fils afin de rajouter soit un saut de ligne (dans le cas du premierfils élément), soit la même indentation que la balise ouvrante de l'élément père affectéeà la balise fermante. Si, au lieu de :if ((enfants.item(0) != null) &&

(enfants.item(enfants.getLength()-1).getNodeType() ==

Node.ELEMENT_NODE)) {writer.write(niveauIndentation);

on écrivait simplement ceci:writer.write(niveauIndentation)

on obtiendrait alors des espaces blancs (autant qu'en comporte l'indentation courante)à la fin de chaque contenu textuel d'élément. En effet, comme il a déjà été dit, lecontenu textuel d'un élément est considéré comme noeud fils de cet élément. Aussi, unélément ne contenant que du contenu textuel obéit à la condition if (enfants !=null) et nous oblige à tester une condition supplémentaire (le fait que le dernier filsde l'élément considéré soit de type Element) avant de définir l'indentation de la balise

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 23

Page 24: Tutoriel DOM et JDOM

de fermeture.

Il nous faut tester ensuite les noeuds de type Text, CDATA, COMMENT, PROCESSINGINSTRUCTION, , ENTITY REFERENCE, principalement via la méthodegetNodeValue().case Node.TEXT_NODE:

writer.write(noeud.getNodeValue());break;

case Node.CDATA_SECTION_NODE:writer.write("<![CDATA[" +

noeud.getNodeValue() + "]]>");break;

case Node.COMMENT_NODE:writer.write(niveauIndentation + "<!-- " +

noeud.getNodeValue() + " -->");writer.write(sautLigne);break;

case Node.PROCESSING_INSTRUCTION_NODE:writer.write("<?" + noeud.getNodeName() +

" " + noeud.getNodeValue() +"?>");

writer.write(sautLigne);break;

case Node.ENTITY_REFERENCE_NODE:writer.write("&" + noeud.getNodeName() + ";");break;

Il faut noter que le noeud de type Processing Instruction est un peu particulier,puisqu'il requiert à la fois les méthodes getNodeName() et getNodeValue() pourêtre correctement affiché en sortie. Si nous n'utilisons que getNodeName(), nousobtenons l'écran suivant en sortie:

alors que si nous n'utilisons que getNodeValue(), nous obtenons dans ce cas lerésultat suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 24

Page 25: Tutoriel DOM et JDOM

Enfin, il nous faut considérer le cas des noeuds de type DocumentType, quireprésentent des déclarations DOCTYPE. Comme on peut y trouver des donnéesspécifiques, il faut veiller à transtyper l'instance de Node vers l'interfaceDocumentType afin d'accéder à ces données supplémentaires. Les méthodes àutiliser sont alors getName(), getPublicId() pour récupérer l'identifiant public (s'ilexiste), et getSystemId() pour l'ID système de la DTD référencée. on obtient alorsle code suivant:case Node.DOCUMENT_TYPE_NODE:

DocumentType docType = (DocumentType)noeud;writer.write("<!DOCTYPE " + docType.getName());if (docType.getPublicId() != null) {

System.out.print(" PUBLIC \"" +docType.getPublicId() + "\" ");

} else {writer.write(" SYSTEM ");

}writer.write("\"" + docType.getSystemId() + "\">");writer.write(sautLigne);break;

Il ne reste plus dès lors qu'à compléter notre fichier DOMSerialiseur.java et d'yplacer l'instruction suivante: serialiseur.serialise(document,fichierSortie); à la place du commentaire //Sérialise précédemment mis pardéfaut (voir le panneau Sérialisation on page 19 ).

Si l'on veut d'autre part changer la valeur des variables privées indentation etsautLigne, il suffit d'ajouter à la suite par exemple le code suivant:serialiseur.setIndentation(" ");serialiseur.setSautLigne("\n\n");

En exécutant la ligne suivante dans la fenêtre DOS:java Serialiseur catalogue.xml sortie.xml

, nous obtenons alors le fichier sortie.xml suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 25

Page 26: Tutoriel DOM et JDOM

Les fichiers source sont Serialiseur.java et DOMSerialiseur.java

DOM et JAXP 1.1 (parsing)Regardons à présent ce que l'API de Sun JAXP (JAVA API for XML Parsing)peut nousapporter par rapport aux tâches que nous avons déjà vues. (Toutes les versionsrécentes des parseurs prennent en charge cet API, il n'est donc pas nécessaire de latélécharger séparément: toutefois, JAXP est disponible en téléchargement sur le sitede Sun ou de la fondation apache sous forme de l'archive jaxp.jar). Avant de rentrerdans le détail du code, il est important de mentionner le fait que JAXP se situe à unniveau supérieur par rapport à l'API DOM (ou SAX ou JDOM que nous verronsprochainement), et que, de ce fait, elle n'offre aucune manière d'analyser du codeXML, tâche qui reste dévolue aux trois API mentionnées. JAXP permet seulement derendre beaucoup plus accessibles et manipulables certaines fonctionnalitésdont DOM ou les autres API standard s'acquittent plutôt difficilement.

Premièrement, parser un document XML se réalise avec plus de simplicité: avant, ons'en souvient (panneau Récupération de noeuds on page 6 ), il nous fallait avant toutcréer une instance de DOMParser en utilisant l'implémentation d'un analyseurspécifique à un vendeur (nous avions ainsi utilisé Xerces de la fondationapache.org), opération qui nous oblige à changer le code et le recompiler en cas demodification de parseur. L'API JAXP de Sun propose de ce point de vue une bienmeilleure alternative, puisqu'elle permet d'utiliser la classe d'analyse d'un vendeursous la forme d'une propriété système Java: ainsi, JAXP fournit un mode d'analyse

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 26

Page 27: Tutoriel DOM et JDOM

neutre par rapport aux vendeurs.

On se rappelle le code de notre programme récupérant les titres des livres ducatalogue://DOMimport org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NodeList;//Importation de l'analyseur xercesimport org.apache.xerces.parsers.DOMParser;public class TestDOM{

public static void main( String [] args ) throws Exception{

DOMParser parser = new DOMParser();parser.parse("catalogue.xml");Document document = parser.getDocument();Element catalogue = document.getDocumentElement();NodeList titres = catalogue.getElementsByTagName("titre");System.out.println("Les titres des livres du catalogue sont: ");for (int i=0; i<titres.getLength(); i++) {System.out.println(titres.item(i).getFirstChild().getNodeValue());}

}}

Les trois lignes surlignées en gras font appel aux classes spécifiques de Xerces: enfait, comme il a déjà été dit, la spécificaton DOM ne fournit pas de standard pourobtenir un noeud de type Document. C'est à ce niveau précis qu'intervient JAXP, àtravers l'utilisation des classes javax.xml.parsers etjavax.xml.parsers.DocumentBuilder. L'approche de base est la suivante:

* Utilisation de la méthode de constructionDocumentBuilderFactory.newInstance() afin de retourner un objetDocumentBuilderFactory

* Utilisation de la méthode newDocumentBuilder() de cet objetDocumentBuilderFactory afin de retourner une instance (spécifique d'unvendeur) de la classe abstraite DocumentBuilder

* Uitlisation d'une des méthodes parse() de DocumentBuilder afin de lire ledocument XML et retourner un objet org.w3c.dom.Document.

La version JAXP du code précédent donne alors ceci://DOMimport org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NodeList;import javax.xml.parsers.FactoryConfigurationError;import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.DocumentBuilder;public class TestDOMJAXP{

public static void main( String [] args ) throws Exception{try{

//Récupère une instance de la classe de fabricationDocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//Récupére une instance de la classe DocumentBuilder

(spécifique vendeur)

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 27

Page 28: Tutoriel DOM et JDOM

DocumentBuilder parser = factory.newDocumentBuilder();//effectue le parsing avec récupération du noeud DOM DocumentDocument document = parser.parse("catalogue.xml");//code identique au précédentElement catalogue = document.getDocumentElement();NodeList titres = catalogue.getElementsByTagName("titre");System.out.println("Les titres des livres du catalogue sont: ");for (int i=0; i<titres.getLength(); i++) {System.out.println(titres.item(i).getFirstChild().getNodeValue());}}

catch (FactoryConfigurationError e) {System.out.println("Impossible de localiser une classe de construction");}catch (ParserConfigurationException e) {System.out.println("Impossible de localiser un parseur JAXP");}

}}

En exécuant TestDOMJAXP disponible ici, on vérifie que l'on obtient bien le mêmerésultat:

Il faut noter que la classe DocumentBuilderFactory possède un certain nombred'options de configuration. Parmi les plus importantes, on compte la prise encharge des espaces de noms par le parseur via la méthode public booleanisNamespaceAware() qu'on rend effective par le code suivant:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setNamespaceAware(true);

La validation d'un document XML par rapport à une DTD peut également être prise encharge par le parseur produit par une factory via la méthode public booleanisValidating() mise en oeuvre de la faÇon suivante:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setValidating(true);

Il existe enfin des otions concernant la possibilité ou non d'ignorer lescommentaires situés dans le document d'entrée (public booleanisIgnoringComments(), la possibilité ou non de résoudre les référencesd'entités (public boolean isExpandEntityReference), , etc.

Enfin, pour terminer ce petit tour d'horizon sur l'analyse de documents XML via JAXP, ilconvient de revenir sur le point important suivant: comment JAXP choisit-il sonparseur?

En fait, JAXP utilise le parseur que référence la classe indiquée par la propriétésystème javax.xml.parsers.DocumentBuilderFactory. Par exemple, si l'onveut être sûr que que nous utilisons Xerces lors de l'analyse de notre documentcatalogue.xml via notre classe TestDOMJAXP, le plus simple est d'exécuter cette

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 28

Page 29: Tutoriel DOM et JDOM

dernière de la faÇon suivante: java-Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImplTestDOMJAXP, comme indiqué par l'écran ci-dessous:

Si l'on veut une fois pour toutes que JAXP utilise tel parseur plutôt que tel autre sansavoir à le spécifier à chaque fois en ligne de commande comme nous venons de lefaire, il suffit de créer dans le répertoire lib de votre installation Java un fichiernommé jaxp.properties, qui contiendrait les informations suivantes:javax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.DocumentBuilderFactory(xerces)=org.apache.crimson.jaxp.DocumentBuilderBuilderFactory(crimson)

Si auncun renseignement n'est fourni d'une faÇon ou d'une autre, alors ce seral'implémentation de JAXP (par exemple Sun ou Apache) qui détermineral'analyseur par défaut (respectivement Crimson ou Xerces).

DOM et JAXP 1.1 (sérialisation)Bien que le P de JAXP corresponde à Parsing (indiquant par là que JAXP nes'occuperait que de l'analyse), il vaut la peine de noter que l'une des grandesnouveautés apportées par JAXP1.1 par rapport à l'API JAXP1.0 est que cette nouvelleversion rend désormais possible des transformations XML neutres en termes devendeurs, via l'API TrAX contenue dans le paquetage javax.xml.transform.

Du point de vue qui nous oocupe (ici celui de la sérialisation), il faut avouer que JAXPne dipose pas en tant que telle d'une classe de sérilaisation, mais l'astuce consiste àutiliser une transformation à vide à partir du document XML initial pour arriverexactement au même résultat. Regardons cela d'un peu plus près: L'API JAXP jouitd'une grande cohérence et les instructions à fournir pour exécuter une transformationobéissent exactement à la même dynamique que celles exécutées dans le cas del'analyse, à savoir :

1. Utilisation dela méthode de fabricationTransformerFactory.newInstance() retournant un objetjavax.xml.transform.TransformerFactory

2. Utilisation de la méthode newTransformer() de cet objetTransformerFactory afin de retourner une instance (spécifique du vendeur)de la classe abstraite javax.xml.transform.Transformer

3. Réalisation des opérations de transformation

Illustrons cela par un exemple simple: nous parsons une des versions du fichiercatalogue.xml, le modifions par quelques manipulations DOM afin de lui rajouter unlivre dans la liste du catalogue, et finalement le sérialisons vers l'écran ou un fichier desortie.

Pour cela, la première chose à faire d'importer les classes de JAXP relatives aux

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 29

Page 30: Tutoriel DOM et JDOM

transformations (ces classes concernent plus spécifiquement l'API TrAX)://importation des classes TrAXimport javax.xml.transform.TransformerFactory;import javax.xml.transform.Transformer;import javax.xml.transform.Source;import javax.xml.transform.Result;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import javax.xml.transform.TransformerException;

En plus des classes de constructions (à mettre en parallèle avec les classes d'analyse)et d'exceptions, on doit rajouter ici les classes relatives aux entrées et aux sorties de latransformation TrAX. Nous fournissons dans notre exemple une arborescence DOMen entrée de transformation, ce qui explique l'importation de la classejavax.xml.transform.dom.DOMSource. (Celle-ci constitue, ainsi que les deuxautre classes d'entrée javax.xml.transform.sax.SAXSource etjavax.xml.transform.stream.StreamSource, une implémentation concrète del'interface javax.xml.transform.Source). Comme nous envisageons d'effectuerla sortie sur l'écran système ou dans un fichier, nous utilisons la classe de sortiejavax.xml.transform.stream.StreamResult qui prend comme arguments soitun OutputStream (comme System.out), soit un Writer. Cette classe de sortieconstitue, ainsi que les classes javax.xml.transform.dom.DOMResult etjavax.xml.transform.sax.SAXResult, une implémentation concrète del'interface javax.xml.transform.Result

Ensuite, nous modifions notre document d'origine en rajoutant un élément <livre>,ainsi qu'un <titre> et un <auteur>. Il n'y a rien de difficile, simplement bien veiller àcréer chaque nouveau Node à partir du document DOM lui-même via la méthodecreateElement() et ne placer qu'ensuite le noeud ainsi crée dans l'arborescenceDOM.Element catalogue = document.getDocumentElement();

//création d'un élément livreElement livre = document.createElement("livre");//création d'un élément titreElement titre = document.createElement("titre");//création d'un élément auteurElement auteur = document.createElement("auteur");//ajout d'un contenu textuel à l'élément titre créetitre.appendChild(document.createTextNode("Le mythe de Sisyphe"));//ajout d'un contenu textuel à l'élément auteur créeauteur.appendChild(document.createTextNode("Albert Camus"));//ajout de l'élément titre ainsi crée à l'élément livrelivre.appendChild(titre);//ajout de l'élément auteur ainsi crée à l'élément livrelivre.appendChild(auteur);//ajout de l'élément livre à l'élément cataloguecatalogue.appendChild(livre);

Nous passons ensuite à la sérialisation proprement dite, dont on a déjà dit qu'elleconsistait en fait via JAXP à une transformation sans feuille de style. Les étapes sontles suivantes:

1. Obtenir une classe fabriquant des instances de la classe Transformer via laclasse TransformerFactory

2. Récupérer une instance de la classe Transfomer3. Réaliser les opérations de transformation, en définissant les types de document

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 30

Page 31: Tutoriel DOM et JDOM

d'entrée et de sortie, et en utilisant ceux-ci avec la classe transform()

//Création d'un objet TransformerFactory fabriquant des instances de la classe TransformerTransformerFactory tfactory = TransformerFactory.newInstance();//Création d'un objet TransformerTransformer transformeur = tfactory.newTransformer();//Définition du document d'entrée comme arborescence DOM

Source entrée = new DOMSource(document);//Définition du document de sortie vers l'écranResult sortie = new StreamResult(System.out);transformeur.transform(entrée, sortie);

L'exécution de la classe JAXPSerialise produit le résultat suivant:

Si l'on veut effectuer la sortie vers un fichier sortie.xml, il suffit de changer la lignede définition de sortie: Result sortie = new StreamResult(newFileOutputStream("sortie.xml");

DOM Niveau 2: les espaces de nomDOM Niveau 2 apporte comme complément susbantiel par rapport au niveauprécédent la prise en charge des espaces de nom. On a déjà indiqué dans lepanneau Création et Modification d'une arboresence DOM (via servlet) on page 13l'utilisation de la méthode createDocument() introduite par DOM Niveau 2, et quiprend comme premier argument l'URI de l'espace de nommaage de l'élément racine etcomme second argument le nom qualifié (qualified name)d'un tel nom: on rappelleque le nom qualifié représente la concaténation du nom local de l'élément et dupréfixe associé à l'URI de l'espace de nommage. Parallèlement à cette méthode,DOM niveau 2 met à notre disposition les méthodes de créationcreateElementNS(URI, nom qualifié) et createAttributeNS(URI, nomqualifie). Ainsi, si l'on souhaite utiliser l'URI d'espace de nomhttp://schema-livre et le préfixe livre avec un élément nommé titre, ondevra invoquer la méthode createElementNS("http://schema-livre",

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 31

Page 32: Tutoriel DOM et JDOM

"livre:titre"). On peut également utiliser la méthode getPrefix() pourrécupérer le préfixe du nom qualifié (ici, livre) ainsi que la méthodegetNamespaceURI() pour obtenir l'espace de nom correspondant. Si l'élément setrouve dans un espace de nom par défaut ou qu'il n'est dans aucun espace denommage, la valeur retournée est alors null. Pour illustrer cela, reprenons notreexemple de création/modification de description de livre via la servlet du pannneauCréation et Modification d'une arboresence DOM (via servlet) on page 13 . On veutcréer un document xml avec un espace de nommage par défauthttp://www.catalogue.com. Pour cela, le code à utiliser est le suivant:...String docNS = "http://www.catalogue.com";if (!fichierXML.exists()) {

// Creer une nouvelle arborescence DOMDOMImplementation domImpl = new DOMImplementationImpl();doc = domImpl.createDocument(docNS, "livre", null);Element racine = doc.getDocumentElement();

...

Comme il a été expliqué plus haut, cela aurait normalement pour effet de créer unélément racine du document avec un espace de nom par défaut référant à l'URIhttp://www.catalogue.com. Mais si vous compilez et exécutez le code, vous vousapercevez que votre document xml n'a subi aucun changement. C'est parce qu'il fautajouter à la main l'attribut xmlns à l'arboresence DOM, laquelle API ne prend pasen charge par elle-même un tel ajout. i faut donc rajouter:racine.setAttribute("xmlns",docNS)

Si nous voulons rajouter un autre espace de nom préfixé "livre" à certains deséléments du document, il faudra donc également commencer par déclarer un telespace de nom sur l'élément racine via la ligne de code suivante:String NS ="http://schema-livre";racine.setAttribute("xmlns:livre", NS);

Ensuite, on place les éléments <titre> et <auteur> dans l'espace de nom préfixépar "livre" et l'élément <description> dans l'espace de nom défini par défaut de lamanière suivante://Titre du livre

Element elementTitre = doc.createElementNS(NS, "livre:titre");Text texteTitre = doc.createTextNode(titre);elementTitre.appendChild(texteTitre);racine.appendChild(elementTitre);// Auteur du livreElement elementAuteur = doc.createElementNS(NS,"livre:auteur");Text texteAuteur = doc.createTextNode(auteur);elementAuteur.appendChild(texteAuteur);racine.appendChild(elementAuteur);// Description du livreElement elementDescription = doc.createElementNS(docNS,"description");Text texteDescription = doc.createTextNode(description);elementDescription.appendChild(texteDescription);racine.appendChild(elementDescription);

Attention, dans la deuxième partie de la servlet concernant la modification d'unearborescence DOM, on utilise les méthodes DOM Niveau 2getElementsByTagNameNS(URI,nom local). Le deuxième argument de cette

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 32

Page 33: Tutoriel DOM et JDOM

thode n'est pas, contrairement à la méthode createElementNS(URI, nomqualifié) un nom qualifié (incluant donc un préfixe) mais un nom local ( sanspréfixe). Donc le code ressemblera à cela:else {

// Charge le documenttry {

DOMParser parser = new DOMParser();parser.parse(fichierXML.toURL().toString());doc = parser.getDocument();Element racine = doc.getDocumentElement();

// Titre du livreNodeList elementsTitre = racine.getElementsByTagNameNS(NS,"titre");Element elementTitre = (Element)elementsTitre.item(0);Text texteTitre = (Text)elementTitre.getFirstChild();texteTitre.setData(titre);// Auteur du livreNodeList elementsAuteur = racine.getElementsByTagNameNS(NS,"auteur");Element elementAuteur = (Element)elementsAuteur.item(0);Text texteAuteur = (Text)elementAuteur.getFirstChild();texteAuteur.setData(auteur);// Description du livreNodeList elementsDescription =

racine.getElementsByTagNameNS(docNS,"description");Element elementDescription = (Element)elementsDescription.item(0);// Supprime et recrée la description

racine.removeChild(elementDescription);elementDescription = doc.createElementNS(docNS,"description");

Text texteDescription = doc.createTextNode(description);elementDescription.appendChild(texteDescription);

racine.appendChild(elementDescription);....

En compilant et en exécutant cette servlet MiseAJourServletNS.java et en pointant surl'adresse http://localhost:8080/updateNS (nécessitant la modification dufichier web.xml), et en remplissant le formulaire de la faÇon suivante:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 33

Page 34: Tutoriel DOM et JDOM

on obtient alors le fichier livre-01.xml suivant, situé dans le dossier C:\DOM\tuto:

Module Traversal de DOM Niveau 2: NodeIteratorLa spécification DOM Niveau 2 regroupe six modules destinés à étendre lesfonctionnalités de DOM Niveau 1. Parmi ces derniers, en plus de ce qu'offre le noyaude DOM Niveau 2, on retrouve le module Traversée, facilitant, comme son noml'indique, la "traversée" ou la navigation au sein d'un arbre DOM. Cette extension DOMs'avère particulièrement utile lorque la structure du document DOM à analyser nousreste à peu près inconnue, et qu'il nous faut récupérer tout de même des noeuds oumodifier le contenu d'un tel document. Les classes qui constituent le module DOMTraversal se trouvent toutes dans le package org.w3c.dom.traversal , et sont aunombre de quatre. La classe de base à partir de laquelle tout est construit se nommeDocumentTraversal(un peu de la même manière que dans le noyau DOM, toutcommence avec une inteface Document), les trois autres sont NodeIterator,TreeWalker et NodeFilter laquelle classe est utilisée pour personnaliser la naturedes noeuds retournés lors du parcours de l'arbre DOM. Voyons comment cela marche:

Soit le document catalogueTraversal.xml, pour lequel, à chaque livre, on ajoute unélément <description>, lequel contient lui-même quelques mots-clés, signaliséspar une balise de type <mot-clé>: le but est de récupérer le plus facilement etdirectement possible de tels mot-clés. La première chose à faire est de créer uneinstance de DocumentTraversal:en général, la classe qui implémente l'interfaceorg.w3c.dom.Document est celle qui implémente également DocumentTraversal//Accéder à l'implémentation org.w3c.dom.Document du parseur (ici xerces)Document document = new org.apache.xerces.dom.DocumentImpl();//Récupérer une instance de DocumentTraversal par cast de typeDocumentTraversal traversal = (DocumentTraversal)document

Ensuite, il faut créer une implémentation de l'interfaceorg.w3c.dom.traversal.NodeFilter en implémentant son unique méthodepublic short acceptNode(Node n). Cette méthode accepte un Node commeseul argument: elle traite ce noeud et retourne un constante Java short, indiquant sile noeud rencontré doit être retourné au NodeIterator courant ou non.L'implémentation suivante de NodeFilter accepte uniquement les noeuds àl'intérieur des éléments <mot-clé>:import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.traversal.NodeFilter;class NodeFilterPerso implements NodeFilter {public short accept(Node n) {

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 34

Page 35: Tutoriel DOM et JDOM

if (n.getNodeType() == Node.TEXT_NODE) {Node parent = n.getParentNode();if (parent.getNodeName().equalsIgnoreCase("mot-clé")) {

return FILTER_ACCEPT;}

}//Si nous arrivons là, nous ne sommes pas intéréssés

return FILTER_SKIP;}}

Fichier source de NodeFilterPerso.java. Ici, nous utilisons un code DOM habituel: nousne nous intéressons qu'aux noeuds textuels, et souhaitons récupérer le contenu textueldes éléments <mot-clé>, non aux éléments eux-mêmes. Pour ce faire, nous utilisonsla méthode getNodeName() appliquée au parent du noeud textuel rencontré (il estraisonnable de présupposer que le parent d'un noeud textuel est un élément). Si lenom de l'élément parent est mot-clé, alors le code renvoie la valeurFILTER_ACCEPT. Sinon, il renvoie la valeur FILTER_SKIP qui évite le noeud examinémais continue à itérer sur ses fils. (Il existe une troisième valeur de retour,FILTER_REJECT, qui rejette le noeud examiné ainsi que tous ses fils, et qui n'estapplicable qu'à TreeWalker que nous verrons plus loin).

Une fois ce filtre crée, il reste à créer un NodeIterator utilisant ce filtre. Il y aplusieurs informations à fournir dans le cadre d'une telle création: d'abord l'élément surlequel démarrer l'itération: ici, nous choisissons par défaut l'élément racine dudocument xml. Ensuite, il faut indiquer la nature des noeuds que l'itérateur doitafficher:: il peut s'agir soit de tous noeuds (NodeFilter.SHOW_ALL), soit uniquementles éléments (NodeFilter.SHOW_ELEMENT), soit encore les valeurs textuelles(NodeFilter.SHOW_TEXT). Enfin, on spécifie l'implémentation du NodeFilter quinous convient, et l'on rend possible l'expansion ds références d'entité, via la valeurbooléeene true.// Parse into a DOM tree

DOMParser parser = new DOMParser();parser.parse(nomFichier);Document document = parser.getDocument();// Indique le noeud à partir duquel commencer l'itérationElement racine = document.getDocumentElement();// Crée un NodeItertorNodeIterator i = ((DocumentTraversal)document)

.createNodeIterator(racine, NodeFilter.SHOW_ALL,new NodeFilterPerso(), true);

Node n;while ((n = i.nextNode()) != null) {

System.out.println("mot-clé trouvé: '" + n.getNodeValue() + "'");

La caractéristique vraiment intéressante à remarquer à propos de l'utilisation deNodeIteraor est que l'on récupère tous les noeuds formant la descendance del'élément racine, même sur plusieurs niveaux de profondeur...Ce qui s'avèreextrêmement utile lorque l'on ne connaît pas l'arborescence XML! Lorque l'on compilepuis exécute le code de RechercheMot.java, via la ligne de commande javaRechercheMot catalogueTranversal.xml, on obtient alors l'écran suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 35

Page 36: Tutoriel DOM et JDOM

N.B: Il vaut la peine de remarquer qu'il peut y avoir des "interférences" entre l'utilisationde la constante fournie à la méthode createNodeIterator() et l'implémentation duNodeFilter. Dans l'exemple ci-dessus, on a utilisé une constanteNodeFilter.SHOW_ALL alors que l'on retournait des éléments textuels, ce qui estconcordant. Par contre, si l'on tente de changer la constante enNodeFilter.SHOW.ELEMENT à la méthode createNodeIterator(), on ne reÇoitaucun mot-clé en réponse: l'explication vient du fait que l'implémentation duNodeIterator() reÇoit des noeuds de type contenu textuel et qu'il n'est censéafficher que des éléments. Une faÇon correcte de faire serait ici de passer la constanteNodeFilter.SHOW_TEXT

Module Traversal de DOM Niveau 2: TreeWalkerL'interface TreeWalker est essentiellement semblable à l'interface NodeIteratorprécédemment vue: la seule différence consiste en ce que l'on récupère ici une vuearborescente plutôt qu'une vue sous forme de liste. Il s'git là encore de traiter unearborescence DOM standard en la filtrant pour n'obtenir qu'une certaine vue de cettearborescence, dépouillée de tel ou tel type d'élément, ou bien dénué descommentaires, etc. La faÇon de créer une instance de TreeWalker est équivalente àcelle précédemment vue:on utilise simplement la méthode createTreeWalker enlieu et place de la méthode createNodeIterator()// Parse un fichier en une arborescence DOM

File file = new File(nomFichier);DOMParser parser = new DOMParser();parser.parse(nomFichier);Document document = parser.getDocument();// Indique le noeud à partir duquel commencer l'itérationElement racine = document.getDocumentElement();// Crée un TreeWalkerTreeWalker i = ((DocumentTraversal)document)

.createTreeWalker(racine, NodeFilter.SHOW_ALL,null, true);

Ici, nous n'avons spécifié aucune implémentation personnalisée de NodeFilter. Onpeut simplement naviguer à travers l'arbre DOM en renvoyant à chaque noeudrencontré son nom ainsi que sa valeur. On admire la facilité de la mise en place d'unetelle navigation comparée à un code DOM classique pour lequel il faudrait opérer desrécursions successives à chaque niveau de profondeur.Node n;

while ((n = i.nextNode()) != null) {System.out.println( n.getNodeName() + ":" + n.getNodeValue());

}

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 36

Page 37: Tutoriel DOM et JDOM

Lorque l'on exécute RechercheTreeWalker.java via la ligne de commande javaRechercheTreeWalker catalogueTraversal.xml, on obtient alors l'écransuivant:

On vérifie ainsi qu'un élément ne possède jamais en tant que tel de contenu textue, cequi nous est indiqué par la valeur null retournée par la méthode getNodeValue().

Module Range de DOM Niveau 2Le module Range (Portée) de DOM Niveau 2, dont la spécification se trouve àl'adresse suivante http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html ,est très pratique pour sélectionner le contenu délimité par un point initial et unpoint final d'un document DOM dont on ignore par ailleurs à peu près tout de lastructure. Une fois qu'il est sélectionné, on peut alors tout à faire avec ce fragment decontenu: y insérer des données, le copier ou en supprimer des parties, etc.org.w3c.dom.ranges constitue le paquetage afférent à ce module, et inclutl'interface principale org.w3c.ranges.DocumentRange, l'équivalent de l'interfaceorg.w3c.dom.traversal.DocumentTraversal pour le paquetageorg.w3c.traversal. Si l'on reprend notre exemple de servlet du panneau Créationet Modification d'une arboresence DOM (via servlet) on page 13 . Nous avions alorsdécidé de supprimer complètement l'élément <description> via la méthoderemoveChild() et de le remplacer par un nouveau élément de même nom contenantle texte passé en paramètre. Le module de portée de DOM Niveau 2 permet de faireune opération équivalente de faÇon tout à fait simple. On commmence par lestraditionnelles instructions d'importation des classes DocumentRange et Rangesituées dans le paquetage org.w3c.dom.ranges.// Module Rangeimport org.w3c.dom.ranges.DocumentRange;import org.w3c.dom.ranges.Range;

Ensuite, il faut créer notre portée, exactement de la même faÇon que nous avionsdéfini un NodeIterator ou un TreeWalker, , c'est-à-dire par transtypage et

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 37

Page 38: Tutoriel DOM et JDOM

utilisation d'une méthode de création, ici createRange().//Création d'une portéeRange portee = ((DocumentRange)doc).createRange();

Une fois la portée créée, il faut à présent définir les points d'arrivée et de départ.Comme on veut supprimer tout le contenu de l'élément <description>, on choisitcomme départ ce qui précède le premier fils via la méthode setStartBefore(), etcomme point d'arrivée ce qui succède au dernier fils, via setEndAfter().//Défintion du point de départportee.setStartBefore(elementDescription.getFirstChild());//Définition du point d'arrivéeportee.setEndAfter(elementDescription.getLastChild());

Une fois ce fragment encadré, il ne reste plus qu'à invoquer la méthodedeleteContents pour le supprimer.//Suppression du contenuportee.deleteContents();

Dès lors, il ne reste plus qu'à créer le nouveau contenu textuel et à l'ajouter àl'arborescence via la méthode classique appendChild()Text texteDescription = doc.createTextNode(description);elementDescription.appendChild(texteDescription);

Enfin, on peut libérer les ressources associées à la portée en appelant la méthodedetach(), comme suit:portee.detach();

ON voit l'avantage d'utiliser un module Range dans ce cas-là: si on avait en effet voulugarder en DOM Niveau 1 l'élément description et simplement changé son contenutextuel comme on vient de le faire, il aurait fallu crée une NodeList comprenant tousles noeuds fils de l'élément, puis dans un second temps, il aurait fallu itérer sur chacund'eux pour les supprimer un à un. A l'évidence, le module Portée est beaucoup pluspratique à utiliser. Voir le code source de RangeMiseAJourServletNS.java

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 38

Page 39: Tutoriel DOM et JDOM

Section 3. L'API JDOM

IntroductionJDOM présente de grandes similitudes avec le DOM en ce sens qu'il représente undocument XML via une structure arborescente. Cependant, il s'en distingue parceque JDOM est spécifiquement conÇu pour JAVA et que du point de vue dudéveloppeur JAVA, il s'avère beaucoup plus pratique à utiliser (A ce propos, ilconvient de noter que contrairement à ce qui est parfois écrit, le J de JDOM ne renvoiepas à Java, et que JDOM suit la nomenclature NAA - not an abreviation - de Sun:JDOM veut ainsi dire JDOM et rien d'autre). Nous utilisons ici JDOM1.0 beta 8disponible à l'adresse http://www.jdom.org. La configuration est très simple: lesopérations à effectuer sont spécifiées dans le fichier README.txt de la distribution etsont principalement au nombre de deux:

1. D'abord vérifier que la variable d'environnement JAVA_HOME est définiecorrectement en spécifiant bien le dossier du JDK comprenant la JVM devant êtreutilisée.

2. Ensuite, veiller à être dans le dossier dans lequel est situé le fichier build.xmlpuis taper ./build.sh (unix) ou .\build.bat (windows). Cela a poureffet de créer via l'outil Ant l'archive JDOM.jar contenant toutes les classes del'API. Ensuite, il faut inclure cette archive JDOM.jar, ainsi que xerces.jar,jaxp.jar et xalan.jar fournis dans la distribution dans votre classpath.

Création d'un document XMLNous allons commencer par un exemple très simple, permettant de voir la faÇon dontfonctionne l'API JDOM. Pour commencer, nous allons créer le documentcatalogue.xml de la première section de notre tutoriel.import org.jdom.*;public class tutoJDOM1{

public static void main(String[] args){Element racine = new Element("catalogue");Document document = new Document(racine);System.out.println(document);

}}

On commence par importer le package jdom. Ensuite, nous instantions un objet detype Element (classe org.jdom.Element) à l'aide du constructeur Element(). Laclasse Element possède plusieurs constructeurs. Nous utilisons ici le plus simple, quiprend un String comme paramètre, lequel devient le nom de l'élément (nous verronsplus tard le constructeur qui prend en charge les espaces de nom). Après avoir crée unElement, un objet Document est instantié, avec l'élément crée passé commeargument (sachant qu'un document XML ne peut exister sans élément racine, uneinstance de la classe Element est requise par le constructeur de la classeDocument). Finalement, le code sort le Document sur l'écran. En compilant et enexécutant ce code (tutoJDOM1.java), on obtient le résultat suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 39

Page 40: Tutoriel DOM et JDOM

Le résultat ne ressemble pas vraiment à du XML. Utilisé avec la méthodeSystem.out.println, on sait que le message toString est envoyé à l'objetconcerné. Ici, la documentation de JDOM stipule qu'une telle méthode ne doit êtreutilisée que pour le debugging:"toString: This returns a String representation of theDocument, suitable for debugging." On verra dans le prochain panneau SérialisationJDOM on page 41qu'une sérialisation digne de ce nom doit utiliser l'objetXMLOutputter.

En attendant, il nous faut créer la suite de notre document catalogue.xml. Cela sefait de la manière suivante:Element livre1 = new Element("livre");

Element titre1 = new Element("titre");titre1.setText("La Généalogie de la morale");livre1.addContent(titre1);racine.addContent(livre1);

On commence par créer un élément <livre>, puis un élément <titre>, auquel onajoute un contenu textuel via la méthode setText(), puis on attache chacun desdeux éléments ainsi crées à leur noeud parent respectif via la méthodeaddContent(): on remarque la simplicité de la mise en oeuvre par rapport au DOM,car la création d'un noeud (élément ou texte) ne nécessite pas le recours à l'objetDocument. Il reste que la création de chacun des sous-éléments d'un élément<livre> peut rester quand même un peu pénible, et le mieux est peut-être de créerune méthode AjoutElement() pour systématiser l'ajout d'éléments et de leurcontenu textuel à chaque élément <livre>public void ajoutElement(Element titre, String element, String texte) {

Element elementAjoute = new Element(element);elementAjoute.setText(texte);titre.addContent(elementAjoute);

}

On a plus alors qu'à instancier un objet de la classe et de lui appliquer la méthodeautant de fois que nécessaire:

tutoJDOM2 tuto = new tutoJDOM2();Element racine = new Element("catalogue");Document document = new Document(racine);Element livre1 = new Element("livre");Element titre1 = new Element("titre");titre1.setText("La Généalogie de la morale");livre1.addContent(titre1);racine.addContent(livre1);tuto.ajoutElement(livre1, "auteur", "Friedrich Nietzsche");tuto.ajoutElement (livre1, "édition", "Folio essais");tuto.ajoutElement (livre1, "ISBN", "2-07-032327-7");Element livre2 = new Element("livre");racine.addContent(livre2);tuto.ajoutElement(livre2, "titre", "Réflexions sur la poésie");

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 40

Page 41: Tutoriel DOM et JDOM

tuto.ajoutElement(livre2, "auteur", "Paul Claudel");tuto.ajoutElement (livre2, "édition", "Folio essais");tuto.ajoutElement (livre2, "ISBN", "2-07-032327-7");

Sérialisation JDOMUne fois notre strcture créée, il serait bien de pouvoir la visualiser sur l'écran ou del'envoyer en sortie vers un fichier. L'API JDOM utilise la classe XMLOutputter pourenvoyer le code XML vers un flux encapsulant une connection réseau, un fichier outoute autre structure dans laquelle on souhaite placer du code XML. La classeXMLOutputter s'utilise de la manière suivante:XMLOutputter sortie = new XMLOutputter(" ", true);

sortie.output(document, System.out);

Sans argument, la classe XMLOutputter réalise une sortie directe, généralementsans indentation ni saut de ligne. Le document résultat se trouve donc sur une seuleligne, à l'exception de la déclaration XML. Il exsite plusieurs constructeurs plussophistiqués permettant de gérer cela, parmi lesquelles public XMLOutputter(String indent, boolean newlines); et public XMLOutputter (Stringindent, boolean newlines, String encoding). Le paramètre indentpermet de spécifier le nombre d'espaces à utiliser pour l'indentation, la valeurbooléenne newlines détermine s'il y a lieu d'utiliser les sauts de ligne et enfin, sinécessaire, on peut encore vouloir spécifier un paramètre relatif à l'encodage, quideviendra alors la valeur de l'attribut encoding dans la déclaration XML de début dedocumet. La méthode output prend comme arguments soit une instance deOutputStream, soit une instance de Writer (ic, nous effectuons une sortie versl'écran via System.out). Enfin, il convient de noter que la classe XMLOutputter doitêtre utilisée accompagnée d'un bloc de capture d'exception E/S pour êtrevraiment effective. On obtient donc:try {

XMLOutputter sortie = new XMLOutputter(" ", true);sortie.output(document, System.out);}catch (java.io.IOException e){e.printStackTrace();}

En compilant puis exécutant l'ensemble du code tutoJDOM2.java, on obtient la fenêtreDOS suivante:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 41

Page 42: Tutoriel DOM et JDOM

Si l'on veut envoyer les données XML vers un fichier, il faudrait utiliser la classeFileOutputStream, par exemple de la faÇon suivante:

XMLOutputter outputter = new XMLOutputter(" ", true);FileOutputStream sortie = new FileOutputStream("sortie.xml");outputter.output(document,sortie);sortie.flush();sortie.close();

JDOM et les attributsAprès avoir vu comment créer et insérer des éléments et des contenus textuels,voyons à présent comment ajouter des attributs à notre XML. On utilise pour cela leconstructeur new Atribute avec comme paramètres le nom de l'attribut et sa valeur.Dans un deuxième temps, on affecte l'attribut à l'élément correspondant à l'aide de laméthode setAttribute() prenant comme paramètre l'attribut que l'on désireattacher. Dans notre cas, il faudra donc écrire le code suivant pour ajouter les attributslangue="fr" aux deux éléments <livre>:Attribute langue1 = new Attribute("langue", "fr");

Attribute langue2 = new Attribute("langue","fr");titre1.setAttribute(langue1);Element titre2 = livre2.getChild("titre");titre2.setAttribute(langue2);

On commence par créer l'attribut de nom langue en lui affectant la valeur fr, puis onl'affecte à l'élément titre1 déjà défini plus haut dans le code. Ensuite, on crée unnouvel attribut que l'on affecte au titre du deuxième livre: celui-ci n'avait pas étéexplicitement défini dans le code précédent, il faut donc d'abord le récupérer avec laméthode getChild(). En compilant et exécutant le code ainsi obtenu, on obtient lerésultat suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 42

Page 43: Tutoriel DOM et JDOM

Le code source de tutoJDOM3.java est disponible ici.

JDOM et les espaces de nomJDOM prend en charge les espaces de nom à l'aide de l'utilisation de l'un des troisconstructeurs prévus à cet effet. Les deux premiers n'utilisant qu'un String passé enparamètre, comme ceci: Element livre1 = new Element ("livre","www.catalogue-schema.com"); Element livre2 = new Element("livre", "ctg", "www.catalogue-schema.com"); Le premier élément estcrée en passant en paramètre le nom de l'élément, ainsi que l'URI de l'espace de nomauquel il appartient, tandis que le second incluera le préfixe de l'espace de nom, enplus du nom de l'élément et de l'URI auquel l'espace de nom renvoie. Le résultat quel'on obtient en compilant et exécutant tutoJDOM4.java est le suivant:

La troisième faÇon de créer un élément avec un espace de nom consiste à utiliser laclasse Namespace. On crée d'abord un objet Namespace à l'aide de la méthodegetNamespace(), puis on utilise un tel objet pour créer le nouvel élément. Il existetout comme précédemment une version de Namespace pour créer des espaces denoms sans préfixe (les espaces de nom par défaut), et une version pour créer desespaces de nom avec préfixes. Le code à utiliser pour produire le même résultat queprécédemment serait alors le suivant://Crée un espace de nom sans préfixe

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 43

Page 44: Tutoriel DOM et JDOM

Namespace espaceNom = Namespace.getNamespace("www.catalogue-schema.com");Element livre1 = new Element ("livre", espaceNom);//Crée un espace de nom avec préfixeNamespace espaceNomctg = Namespace.getNamespace("ctg", "www.catalogue-schema.com");Element livre2 = new Element ("livre", espaceNomctg);

La méthode getChildren(Element element, Namespace espaceNom) estégalement utilisée pour la recherche d'éléments associés à tel ou tel espace denom. Par exemple, pour récupérer l'ensemble des éléments <livre> associés àl'espace de nom www.catalogue-schema.com, il suffit d'invoquer le code suivant:List selection = racine.getChildren("livre", espaceNom);

System.out.println("nombre d'éléments: " + selection.size());for (Iterator i= selection.iterator(); i.hasNext();) {Element courant= (Element)i.next();String nom = courant.getName();System.out.println("Element: " + nom + " titre: " + courant.getChild("titre").getTextTrim());

La méthode getChildren() renvoie une List Java (et non une NodeListspécifique DOM), sur laquelle il est possible de naviguer via un Iterator Javaclassique. Ici, deux choses sont à noter:

1. Premièrement, bien que l'on fasse la recherche uniquement sur l'espace de nomespaceNom (espace de nom par défaut), le résultat de la requête renvoieégalement l'élément associé au préfixe ctg. En effet, avec JDOM, lacomparaison entre les espaces de nom se base uniquement sur les URI.Autrement dit, deux objets Namespace sont considérés comme égaux si leur URIle sont aussi, indépendamment des préfixes. Cette approche est tout à faitconforme à la spécification des espaces de noms XML, laquelle indique que deuxéléments se trouvent dans le même espace de nom si leur URI est identique, etce indépendamment du nom du préfixe auquel cet URI renvoie

2. Deuxièmement, on récupère le contenu textuel d'un élément directement à partirde l'élément (et non en redescendant d'un niveau comme en DOM) via laméthode getTextTrim(), qui est équivalente à la méthode getText(), saufqu'elle retourne le contenu textuel d'un élément sans les espaces blancs quil'entourent ni les espaces superflus entre les mots qu'elle comprimera en espaceblanc unique.

Lorqu'on exécute le code tutoJDOM5.java, on obtient le résultat suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 44

Page 45: Tutoriel DOM et JDOM

JDOM et DTDIl se peut que vous vouliez ajouter une DTD à votre document. Cela est très facile viaJDOM. Par exemple, si on voulait ajouter une DTD catalogue.dtd à notre fichiercatalogue.xml, il nous faudrait juste écrire le code suivant:DocType type = new DocType("catalogue", "catalogue.dtd");Document document = new Document (racine, type);

En exécutant le code de tutoJDOM6.java, on obtient le résultat suivant:

On remarque immédiatement quelque chose d'assez surprenant: bien que nousn'ayons jamais crée de fichier catalogue.dtd, JDOM n'a émis aucun messaged'erreur. La raison en est que JDOM ne valide pas lui-même le XML lorsqu'il estconstruit. Afin de valider un document, on a besoin d'utiliser un parseur SAX ou DOMsous-jacent. Regardons à présent comment cela marche à l'aide du fragment de codesuivant: (On présuppose que l'on a modifié le code de sortie du panneau précédent en:try {

XMLOutputter sortie = new XMLOutputter(" ", true, "iso-8859-1");sortie.output(document, new FileOutputStream("catalogueDTD.xml"));}catch (java.io.IOException e){e.printStackTrace();}

afin de générer le fichier catalogueDTD.xml).import org.jdom.input.*;//Instanciation d'une classe de fabrication SAXSAXBuilder builder = new SAXBuilder(true);//Chargement de la classe et analyse du document catalogueDTD.xmltry{Document document = builder.build(new FileInputStream(catalogueDTD.xml));}catch {JDOMException e){

System.out.println("Erreur de chargement XML" + e.getMessage());}

En compilant et en exécutant le fichier tutoJDOM7.java, voici l'écran qu'on obtient:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 45

Page 46: Tutoriel DOM et JDOM

Ici, il s'avère que notre document XML à valider est un fichier enregistré sur le disqueet que la meilleure faÇon de construire une représentation JDOM dans ce cas se fait àpartir d'un ensemble d'événements SAX. Une solution alternative peut consister àutiliser l'autre classe de construction DOMBuilder mise à notre disposition pourconstruire l'arborescence JDOM, mais ici, c'est une très mauvaise idée pour la bonneet simple raison que DOMBuilder utilise SAX pour construire une arobrescence DOM,avant que cette arborescence DOM ne soit convertie en JDOM. Quand on le peut, enfait toutes les fois où le document à tranformer en JDOM ne consiste pas en unearborescence DOM, le mieux est de se servir de la classe SAXBuilder.

Changer d'API: de DOM à JDOMMême s'il est indéniable que la manipulation JDOM est plus aisée pour un développeurJAVA, il peut arriver, il arrive même fréquemment qu'un tel développeur ait à saressource uniquement des arborescences DOM en entrée, auquel cas il lui faut unmoyen de convertir ce DOM en JDOM. En fait, cela se fait très facilement en passantun objet DOMDocument au builder JDOM, qui renvoie lui-même un DocumentJDOM. Regardons cela de plus près à travers un petit exemple: on récupère dans unpremier temps l'arborescence DOM de notre fichier catalogue.xml, puis on leconvertit en document JDOM par la suite. Le code est le suivant://Création de l'arborscence DOM à partir du fichier catalogue.xml pris pardéfautorg.w3c.dom.Document documentDOM = null;

String fichierXML = "catalogue.xml";if (args.length == 1) {fichierXML = args[0];

}DOMParser parseur = new DOMParser();try {

parseur.parse(new InputSource(fichierXML));documentDOM = parseur.getDocument();}catch(Exception e) {e.printStackTrace();

}

Il n'y a rien de spécial à dire ici: on crée simplement une instance de DOMParseurcomme on l'a vu dans la première section, puis on lui envoie le message parse avecpour argument un objet InputSource de SAX (de manière générale, il est conseilléd'utiliser cette classe à la place d'une simple URI, car elle permet de fournir plusd'informations à l'analyseur: elle permet notamment de résoudre les chemins d'accèsrelatifs au sein d'un document). La conversion de DOM en JDOM s'effectue quant àelle de la manière suivante:

//Conversion du document DOM en document JDOM

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 46

Page 47: Tutoriel DOM et JDOM

try {DOMBuilder builder = new DOMBuilder();org.jdom.Document documentJDOM = builder.build(documentDOM);

XMLOutputter outputter = new XMLOutputter();outputter.output(documentJDOM, System.out);}catch (java.io.IOException e) {e.printStackTrace();

}

Fichier source de tutoJDOM8.java.

Changer d'API: de JDOM à DOML'opération inverse, c'est-à-dire le passage d'une structure JDOM vers unearborescence DOM reste essentiellement identique, mais on utilise alors la classeorg.jdom.output.DOMOutputter, qui s'utilise en prenant comme paramètre unestrcuture JDOM et en renvoyant en sortie une structure DOM, comme ceci:DOMOutputter outputter = new DOMOutputter();org.w3c.dom.Document documentDOM = outputter.output(documentJDOM);

Afin de transformer l'arborescence JDOM de notre fichier catalogue.xml, onpourrait par exemple utiliser le code suivant: On commence par importer les diversclasses et paquetages requis:import org.jdom.output.XMLOutputter;import org.jdom.output.DOMOutputter;import org.jdom.input.SAXBuilder;import org.jdom.JDOMException;import java.io.*;

Ensuite, après avoir déclaré deux variables initialisant deux objetsorg.jdom.Document et org.w3c.dom.Document, nous construisonsl'arborescence documentJDOM via la méthode lectureFichier, en utilisant laclasse SAXBuilder déjà vue.

org.jdom.Document documentJDOM = null;org.w3c.dom.Document documentDOM = null;documentJDOM = lectureFichier(fichierXML);

private static org.jdom.Document lectureFichier(String nom)throws JDOMException

{SAXBuilder sxb = new SAXBuilder();return sxb.build(new File(nom));}

Enfin, on tranforme l'arborescence JDOM en arborescence DOM via un objetDOMOutputter auquel on envoie un message outputDOMOutputter domOutputter = new DOMOutputter();documentDOM = domOutputter.output(documentJDOM);

Code source de tutoJDOM9.java

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 47

Page 48: Tutoriel DOM et JDOM

JDOM et les classes de fabricationJDOM permet de personnaliser ses classes de fabrication, afin de rendre la productiondu code XML en Java plus flexible. Par exemple, on peut vouloir créer une sous-classede la classe par défaut org.jdom.Element afin d'associer à chaque élément crée unespace de nom bien défini une fois pour toutes. Le code pour cela serait le suivant(disponible ici).import org.jdom.Element;import org.jdom.Namespace;public class Elementctg extends Element {

private static final Namespace Espace_ctg = Namespace.getNamespace("ctg", "www.catalogue-schema.com");public Elementctg(String nom) {super(nom, Espace_ctg);

}}

On commence par définir un espace de nom via la méthode getNamespace() vue aupanneau JDOM et les espaces de nom on page 43 . Puis on appelle le constructeur dela classe de base Element, prenant pour paramètre le nom de l'élément à définir.

Une fois en possession de cette sous-classe, il faut maintenant l'utiliser. Cela se faitsimplement en sous-classant l'interface org.jdom.input.DefaultJDOMFactoryqui retourne par défaut toutes les classes essentielles de JDOM. Cela se fait grâce parexemple au code suivant, disponible ici:import org.jdom.Element;import org.jdom.Namespace;import org.jdom.input.DefaultJDOMFactory;class JDOMFactoryPerso extends DefaultJDOMFactory {public Element element(String nom) {return new Elementctg(nom);}

}

L'interface sous-classée JDOMFactoryPerso redéfinit la méthode element del'interface de base en renvoyant un objet Elementctg précédemment défini.

Enfin, troisième étape,lorque l'on a défini une implémentation correcte de l'interfaceDefaultJDOMFactory, il reste à faire appel à la méthode setFactory() afind'indiquer aux deux classes de construction JDOM: SAXBuilder et DOMBuilder,qu'elles doivent l'utiliser. Cela se fait de la faÇon suivante:SAXBuilder builder = new SAXBuilder();JDOMFactory factory = new JDOMFactoryPerso();builder.setFactory(factory);Document document = builder.build(documentXML);

On applique à la classe de construction SAXBuilder notre classe de fabricationpersonnalisée JDOMFactoryPerso via la méthode setFactory(). Les reste estidentique à nos codes de construction de strctures JDOM précédents. Utilisationsuccessive des méthodes build() et output().

En exécutant le code de tutoJDOM10.java à l'aide de la ligne de commande suivantejava tutoJDOM10 catalogue.xml out.xml, on obtient l'écran suivant:

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 48

Page 49: Tutoriel DOM et JDOM

JDOM et XSLTOn a vu dans DOM et JAXP 1.1 (sérialisation) on page 29 qu'une bonne faÇond'effectuer des transformations XSLT avec des arborescences DOM en entrée ou ensortie consistait à utiliser l'API JAXP. Eh bien, il en de même avec JDOM. Les classesJDOM à utiliser en entrée et sortie sont respectivementorg.jdom.transform.JDOMSource(org.jdom.DocumentdocumentJDOMEntree) et org.jdom.transform.JDOMResult(). Soit le codesuivant créant tout d'abord une arborescence JDOM à partir du fichiercatalogue.xml, opérant une transformation XSLT via JAXP à partir d'une tellearborescence, laquelle produit une autre autre structure JDOM en sortie.

On commence par impoter les classes nécessaires://IOimport java.io.*;//TrAXimport javax.xml.transform.*;import javax.xml.transform.stream.StreamSource;//JDOMimport org.jdom.transform.JDOMResult;import org.jdom.transform.JDOMSource;import org.jdom.output.XMLOutputter;import org.jdom.input.SAXBuilder;import org.jdom.JDOMException;

Puis on définit les quelques variables (document JDOM d'entrée, documentJDOM desortie, et String fichierXML valant 'catalogue.xml' par défaut et le premier argumentpassé en ligne de commande sinon), dont on aura besoin dans la suite:org.jdom.Document documentJDOMEntree = null;

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 49

Page 50: Tutoriel DOM et JDOM

org.jdom.transform.JDOMResult documentJDOMSortie = null;String fichierXML = "catalogue.xml";

if (args.length == 1) {fichierXML = args[0];

}

On utilise ensuite la méthode lectureFichier déjà vue, afin de créer unearborescence JDOM, appelée documentJDOMEntreedocumentJDOMEntree = lectureFichier(fichierXML);

On crée ensuite une instance de Transformer en mentionnant la feuille de styles àutiliser par le biais d'un StreamSource approprié, puis on opère la transformationXSLT proprement dite avec les deux arguments que sontorg.jdom.transform.JDOMSource(org.jdom.Document document)(sous-classe de javax.xml.transform.sax.SAXSource) etorg.jdom.transform.JDOMResult (sous-classe dejavax.xml.transform.sax.SAXResult). Pour récupérer le document JDOM issude cette transformation, il faut utiliser la méthode getDocument() appliquée à l'objetJDOMResult issu de la transformation TrAX.TransformerFactory factory = TransformerFactory.newInstance();

Transformer transformer = factory.newTransformer(new StreamSource("catalogue.xsl"));transformer.transform(new org.jdom.transform.JDOMSource(documentJDOMEntree), documentJDOMSortie);org.jdom.Document resultat = documentJDOMSortie.getDocument();

Enfin, on effecue la sérialisation de notre structure JDOM, via XMLOutputter:XMLOutputter outputter = new XMLOutputter(" ",true);

outputter.output(resultat, new FileOutputStream("resultat.xml"));

En utilsant la feuille de style catalogue.xsl suivante:<?xml version="1.0" encoding="iso-8859-1"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:strip-space elements="catalogue"/><xsl:template match="catalogue"><html><h1>CATALOGUE</h1><br/><xsl:apply-templates/></html></xsl:template><xsl:template match="livre"><h2>LIVRE N° <xsl:value-of select="position()"/></h2><table border="1" cellpadding="3"><tr><td><b>TITRE</b></td><td><xsl:value-of select="titre"/></td></tr><tr><td><b>AUTEUR</b></td><td><xsl:value-ofselect="auteur"/></td></tr><tr><td><b>EDITION</b></td><td><xsl:value-ofselect="édition"/></td></tr><tr><td><b>ISBN</b></td><td><xsl:value-of select="ISBN"/></td></tr></table></xsl:template></xsl:stylesheet>

qui produit un tableau pour chacun des livres du catalogue, en indiquant l'ordred'apparition via la fonction position() (on remarque également que l'on a supprimé

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 50

Page 51: Tutoriel DOM et JDOM

tous les noeuds blancs au sein de catalogue.xml via l'instruction<xsl:strip-space elements="catalogue"/>, autrement l'on obtiendrait leslivres 2 et 4), on obtient le résultat suivant en exécutant le code précédent (sourcedisponible ici:

Colophon

This tutorial was written entirely in XML, using the developerWorks Toot-O-Matic tutorialgenerator. The Toot-O-Matic tool is an XSLT stylesheet and several XSLT extensionfunctions that convert an XML file into a number of HTML pages, a zip file, JPEG headinggraphics, and two PDF files. Our ability to generate multiple text and binary formats from asingle source file illustrates the power and flexibility of XML. (It also saves our productionteam a great deal of time and effort.)

Présenté par Cyril Vidal [email protected]

Tutoriel DOM et JDOM Page 51