2. Utilisation de DOM4J

2.1. Introduction

DOM4J est une API Java opensource permettant de lire et écrire du XML, et faire des requêtes XPath sur des documents XML. Elle s'appuie assez largement sur l'API Collections, ce qui la rend très naturelle à utiliser, et beaucoup moins lourde que les API de références que sont Xerces et Xalan. Elle est de plus performante, que ce soit en création de document ou en analyse.

2.2. Un premier exemple simple

Revenons sur notre premier exemple avec DOM4J. Nous avions tout d'abord présenté la lecture d'un fichier XML, voyons à présent la création d’un tel document.

2.2.1. Création d'un document XML en mémoire

Exemple 39. Création d’un document XML en DOM

public Document createDocument() {
	 // création du document
	Document document = DocumentHelper.createDocument() ;
	 // création d'un élément racine, et ajout d'un attribut id de valeur 15
	Element root = document.addElement("marins").addAttribute("id",  "15") ; 
	 // création d'un sous-élément de l'élément racine
	Element marin1 = root.addElement("marin") ;
	 // création d'un sous-élément "nom"
	Element nom1   = marin.addElement("nom") ;
	 // ajout du texte "Surcouf" à cet élément
	nom1.addText("Surcouf") ; 
	 // création d'un sous-élément contenant le texte "Robert"
	 // l'appel est chaîné, car addElement retourne une instance de Element
	Element prenom1 = marin.addElement("prenom").addText("Robert"); 
	
	 return document; 
}

La création d’un document commence par la création d’un objet de type Document, que l’on obtient par invocation d’un méthode factory DocumentHelper.createDocument(). Ce document est vide, il ne possède pas d’élément racine. Pour lui en ajouter un, il faut invoquer la méthode addElement(String) de cet objet. Un document XML ne pouvant contenir qu’un seul élément racine, cette méthode nous retourne un objet de type Element, qui est la racine de ce document XML. On peut ensuite enrichir cet élément :
  • en lui ajoutant des sous-éléments par la méthode Element.addElement(String)) ;
  • en lui ajoutant des attributs : méthode Element.addAttribute(String, String), prenant en paramètre le nom de l’attribut que l’on souhaite ajouter, et sa valeur.
Notons que ces méthodes retournent des objets de type Element, ce qui permet de les chaîner, commme nous l'avons fait dans cet exemple.

2.2.2. Écriture de ce document dans un fichier

L’écriture de ce document reste particulièrement simple.

Exemple 40. Écriture d’un document XML DOM4J dans un fichier

public  void serializetoXML(OutputStream out, String encoding) 
 throws Exception {
	OutputFormat outformat = OutputFormat.createPrettyPrint() ;
	outformat.setEncoding(aEncodingScheme);
	XMLWriter writer =  new XMLWriter(out, outformat);
	writer.write(document);
	writer.flush() ;
}

L’écriture d’un document XML a besoin de deux paramètres : un flux de sortie de type OutputStream et une chaîne de caractères précisant le type d’encodage du fichier. En général cet encodage sera UTF-8. La première étape consiste à construire un formateur, de façon à écrire un fichier XML correctement indenté, et lisible facilement à l’œil nu. Ce formateur est adapté à une lecture du fichier par un humain, mais est légèrement plus consommatrice de ressources. Il n'est donc pas adapté à tous les cas. DOM4J nous fournit un tel formateur, que l’on obtient par appel à la méthode factory OutputFormat.createPrettyPrint(). À l’aide de ces deux objets, il est possible de construire un objet de type XMLWriter, et d’appeler sa méthode write(Document) afin d’écrire le contenu de ce document dans un fichier. Comme on peut le voir, il est possible, en quelques lignes de code, de créer des documents XML et de les écrire sur des flux de sortie, qui peuvent être des fichiers ou des flux HTTP.

2.2.3. Lecture d’un fichier

La lecture d’un fichier XML n’est guère plus compliquée.

Exemple 41. Lecture d’un fichier XML avec DOM4J

public  void parseWithSAX(File file)  throws DocumentException {    	
	SAXReader xmlReader =  new SAXReader() ;
	Document doc = xmlReader.read(file) ;
}

L’opération consiste à créer un reader SAX, et à lui passer un fichier en paramètre. L’objet retourné est de type Document, le même type que nous avons utilisé en création. Nous allons revoir cette interface en détails dans la suite de ce chapitre.

2.3. Organisation de l'API

La structure d'interfaces proposées par cette API suit exactement les différents éléments constitutifs d'un document XML.
  • interface Node : modélise l'intégralité des types de nœud que l'on peut trouver dans un document XML. Une istance de Node peut être enfant d'un élément XML.
  • interface Branch : modélise les types de nœud XML qui peuvent posséder des enfants, à savoir Document et Element.
  • interfaces Text, Attribute, Comment, etc... : modélisent les notions XML correspondantes. Ces interfaces étendent Node.
  • interfaces Document et Element : modélisent le document tout entier et les éléments XML respectivement.
Deux classes échappent à cette hiérarchie : Namespace, qui modélise les espaces de nom, et QName, qui modélise les noms complets ( fully qualified name ).

2.4. Classe Namespace

La première classe que nous allons voir est Namespace. Cette classe permet de modéliser un espace de nom, et elle est utilisée en paramètres de nombreuses méthodes permettant de manipuler les éléments et attributs d’un document XML. Une instance de cette classe est construite par l’utilisation de l’une des deux méthodes factory :
  • get(String prefix, String uri) : retourne l’espace de nom associé à ce préfixe et à cette URI ;
  • get(String uri) : retourne l’espace de noms associé à cette URI.
Ce qui donne par exemple.

Exemple 42. Création d’un espace de noms en DOM4J

Namespace ns = Namespace.get(
	 "galilee", 
	 "http://www.galilee.org/cours-xml") ;

2.5. Classe QName

La deuxième classe que nous voyons est également utilisée dans la plupart des méthodes de manipulation de contenu XML. Elle modélise le nom complet d’un élément appelé fully qualified name (d’où l’appellation qname , comme qualified name ) Un objet de ce type possède trois propriétés : son nom, son préfixe et l’URI de son espace de nom. Ces trois propriétés peuvent avoir pour valeurs :
  • nom : marin
  • nom de l'espace du nom : http://www.galilee.org/cours-xml
  • nom du préfixe : galilee.
On construit un qname à l’aide de l’un de ses trois constructeurs :
  • QName(String) : prend en paramètre le nom complet sous forme d’une chaîne de caractères ;
  • QName(String, Namespace) : prend en paramètre le nom complet et l’espace de nom associé ;
  • QName(String, Namespace, String) : prend en paramètre le nom complet, l’espace de nom associé et le nom complet.
Le concepteur de DOM4J en a fait pour tous les goûts, et l'on peut également utiliser une des méthodes factory suivantes :
  • get(String) : prend en paramètre le nom de l’élément ;
  • get(String, Namespace) : prend en paramètre le nom et son espace de nom ;
  • get(String, Namespace, String) : prend en paramètre le nom, l’espace de nom et le nom complet ;
  • get(String, String, String) : prend en paramètres le nom, le préfixe du nom et l’URI de l’espace de nom.
Notons qu’il existe également d’autres méthodes factory pour construire des qname .

2.6. Interface Node

Cette interface propose un jeu de méthodes permettant de manipuler tous les nœuds d'un document XML. Certaines se rapportent aux requêtes XPath, et nous les verrons plus loin dans ce document. Cette interface expose quatre propriétés :
  • name : le nom de ce nœud ;
  • parent : le parent de ce nœud ;
  • name : le document XML dans lequel se trouve ce nœud ;
  • text : le contenu textuel de ce nœud ;
Ces quatre propriétés sont communes à tous les nœuds XML d'un document. Notons que d'après les spécifications de l'API, la propriété parent ne doit pas être modifiée de l'extérieur de l'élément qui possède ce nœud. Pour détacher un node de son parent, il faut utiliser la méthode detach(), qui détache le nœud de son parent et du document dans lequel il se trouve.

2.7. Interface Branch

Cette interface expose les méthodes nécessaires pour ajouter des enfants au nœud courant, et pour lire ces nœuds.
  • add(...), addElement(...) : permettent d'ajouter des enfants ;
  • remove(...) : permettent de les retirer ;
  • nodeCount(), indexOf(Node), node(int) : permettent d'accéder aux nœuds enfant par son numéro d'ordre dans la liste des enfants ;
  • nodeIterator() : retourne une instance d' Iterator construite sur la liste des enfants de nœud.

2.8. Interface Document

L’interface Document est la première interface que l’on utilise en manipulant des documents XML. Elle étend l'interface Branch, et propose donc toutes les méthodes de manipulation de ses enfants. Elle propose de plus des méthodes pour paramétrer l’ensemble du document XML :
  • addProcessingInstruction() : permet d'ajouter processing instructions à ce document ;
  • xmlEncoding : propriété qui gère le type d’encodage du document (typiquement UTF-8) ;
  • docType, entityResolver : gèrent l’éventuelle DTD ou XML Schema auxquels se document adhère, par l’utilisation d’un EntityResolver.
Enfin, elle propose également deux méthodes : setRootElement(String) et getRootElement(), qui permettent d’accéder à l’unique élément racine de ce document.

2.9. Interface Element

L’interface Element modélise un élément XML. Elle étend l’interface Branch, possédant les méthodes de manipulation des sous-éléments. Un élément peut appartenir à un espace de noms, peut avoir des attributs, des sous-éléments, et du contenu textuel. Cette interface permet donc de manipuler toutes ces notions, tant en écriture qu’en lecture. Les méthodes add(Namespace) et getNamespace() permettent de manipuler l’espace de nom auquel appartient cet élément. Si cet élément possède plusieurs espaces de noms déclarés, on peut accéder aux autres par deux types de méthodes :
  • additionalNamespaces(), qui retourne les espaces de noms autres que celui retourné par getNamespace() ;
  • declaredNamespaces() : retourne enfin l’ensemble des espaces de nom déclarés dans cet élément.
L’ajout d’un attribut peut se faire par les méthodes suivantes :
  • add(Attribute) : permet d’ajouter un attribut construit par ailleurs ;
  • addAttribute(QName, String) : permet de créer un attribut à partir de son nom complet et de sa valeur. Cette méthode retourne l’élément auquel cet attribut a été ajouté ;
  • addAttribute(String, String) : permet de créer un attribut à partir de son nom et de sa valeur. De même, cette méthode retourne l’élément auquel cet attribut a été ajouté.
La lecture d’attributs peut se faire par les méthodes suivantes :
  • attributes() : retourne une List des attributs de cette élément ;
  • attributeIterator() : retourne directement un Iterator sur la liste des attributs de cet élément ;
  • attributeCount() : retourne le nombre d’attributs de cet élément ;
  • attribute(int), attribute(String) et attribute(QName) retournent respectivement l’attribut en n-ième position, l’attribut portant le nom ou le qname passé en paramètre.
La lecture des sous-éléments peut utiliser de nombreuses méthodes :
  • element(String), element(QName) : retourne le sous-élément dont le nom ou le nom complet est passé en paramètre ;
  • elements(), elements(String), elements(QName) : retourne une List des éléments, éventuellement portant le nom passé en paramètre ;
  • elementIterator(), elementIterator(String), elementIterator(QName) : retourne un itérateur sur les sous-éléments, éventuellement portant le nom passé en paramètre.
L’ajout de sous-éléments se fait grâce aux méthodes héritées de l’interface Branch :
  • add(Element) : ajoute l’élément passé en paramètre ;
  • addElement(QName), addElement(String) : ajoute un élément défini à partir de son qname ou de son nom ;
  • addElement(String, String) : ajoute un élément à partir de son nom complet et de l’URI de l’espace de nom associé.
Ces trois méthodes retournent l’élément dans lequel le sous-élément a été ajouté.

2.10. Interface Attribute

L’interface Attribute modélise un attribut d’un élément XML. De la même façon que pour un élément, on peut accéder au qname et à l’espace de nom d’un attribut par les mêmes méthodes. On peut accéder au nom et à la valeur d’un attribut simplement par les deux méthodes getName() et getValue().

2.11. Interface Text

Enfin, l’interface Text modèlise le contenu textuel proprement dit d'un document XML. Elle expose une unique méthode : appendText(String), héritée de l'interface CData.
API Java pour XML
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction
1. Un peu d'histoire
2. Les API Java pour XML
Un premier exemple
1. Introduction
2. Structure de base d’un fichier XML
2.1. Un fichier XML simple
2.2. Notion d’espaces de noms
3. Un premier code pour lire un fichier XML
3.1. Lecture avec Dom4J
Description d’un document XML
1. Introduction
2. Organisation d'un document XML
2.1. Introduction
2.2. Nœuds dans un document XML
2.3. Relations entre nœuds dans un document XML
2.4. Ordre dans un document
2.5. Valeurs atomiques dans un document XML
2.6. Notion de PCDATA et CDATA
3. Document Type Definition (DTD)
3.1. Attacher un document à une DTD
3.2. Contenu d’une DTD
3.3. Exemple de DTD, analyse de web-app_2_3.dtd
4. XML Schema
4.1. Introduction
4.2. Un premier schéma
4.3. Attacher un document XML à un schéma
4.4. Types simples
4.5. Attribut
4.6. Restriction sur une déclaration
4.7. Types complexes
4.8. Contraindre un contenu simple
Lire et écrire du XML
1. Introduction
2. Utilisation de DOM4J
2.1. Introduction
2.2. Un premier exemple simple
2.3. Organisation de l'API
2.4. Classe Namespace
2.5. Classe QName
2.6. Interface Node
2.7. Interface Branch
2.8. Interface Document
2.9. Interface Element
2.10. Interface Attribute
2.11. Interface Text
3. Utilisation de Xerces
3.1. Introduction
3.2. Présentation de SAX
3.3. Présentation de DOM
Analyseur SAX en Xerces
1. Introduction
2. Création d'un analyseur SAX
2.1. Introduction
2.2. Objet DefaultHandler
2.3. Exemple d'analyse d'un fichier
3. Présentation de l'API
3.1. Interface ContentHandler
3.2. Interface ErrorHandler
3.3. Interface DTDHandler
3.4. Interface EntityResolver
3.5. Interface LexicalHandler
Manipuler un DOM en Xerces
1. Introduction
2. Création d'un DOM
3. Présentation de l'API
3.1. Organisation de l'API
3.2. Interface Node
3.3. Interface NodeList
3.4. Interface Element
3.5. Interface Attr
3.6. Interface CharacterData
3.7. Interface Comment
3.8. Interface CDATASection
3.9. Interface Text
3.10. Interface Entity
3.11. Interface EntityReference
3.12. Interface ProcessingInstruction
3.13. Interface Document
3.14. Interface DocumentFragment
3.15. Interface DocumentType
Interroger un document en XPath
1. Introduction, notion de nœud XML
2. Une première requête XPath
2.1. Un premier document à interroger
2.2. Une première requête
2.3. Forme d'une requête XPath