Developpez.com - Java
X

Choisissez d'abord la catégorieensuite la rubrique :

 

Développons en Java   2.10  
Copyright (C) 1999-2016 Jean-Michel DOUDOUX    (date de publication : 19/03/2016)

[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

 

45. Les modèles de documents

 

chapitre 4 5

 

Niveau : niveau 3 Intermédiaire 

 

45.1. L'API JDOM

JDOM est une API open source Java dont le but est de représenter et manipuler un document XML de manière intuitive pour un développeur Java sans requérir une connaissance pointue de XML. Par exemple, JDOM utilise des classes plutôt que des interfaces. Ainsi pour créer un nouvel élément, il faut simplement instancier une classe.

Malgré la similitude de nom entre JDOM et DOM, ces deux API sont très différentes. JDOM est une API uniquement Java car elle s'appuie sur un ensemble de classes de l'API Java notamment celles de l'API Collection.

Le site officiel de l'API est à l'url http://www.jdom.org/

Ce chapitre contient plusieurs sections :

 

45.1.1. L'historique de JDOM

En 2000, Brett McLaughlin et Jason Hunter développent une nouvelle API dédiée aux traitements de documents XML en Java. Le but est de fournir une API plus conviviale à utiliser en Java que SAX ou DOM.

L'historique de JDOM est marquée par plusieurs versions bêta et stables :

JDOM a fait l'objet d'une spécification sous la Java Specification Request numéro 102 (JSR-102) : malheureusement celle-ci n'a pas abouti.

 

45.1.2. La présentation de JDOM

Le but de JDOM n'est pas de définir un nouveau type de parseur mais de faciliter la manipulation au sens large de document XML : lecture d'un document, représentation sous forme d'arborescence, manipulation de cet arbre, définition d'un nouveau document, exportation vers plusieurs formats cibles ...

Dans le rôle de manipulation sous forme d'arbre, JDOM possède moins de fonctionnalités que DOM mais en contrepartie il offre une plus grande facilité pour répondre aux cas les plus classiques d'utilisation.

Cette facilité d'utilisation de JDOM lui permet d'être une API dont l'utilisation est assez répandue.

JDOM est donc un modèle de documents objets open source dédié à Java pour encapsuler un document XML. JDOM propose aussi une intégration de SAX, DOM, XSLT et XPath.

JDOM n'est pas un parseur : il a d'ailleurs besoin d'un parseur externe de type SAX ou DOM pour analyser un document et créer la hiérarchie d'objets relative à un document XML. L'utilisation d'un parseur de type SAX est recommandée car elle consomme moins de ressources que DOM pour cette opération. Par défaut, JDOM utilise le parseur défini par JAXP.

Un document XML est encapsulé dans un objet de type Document qui peut contenir des objets de type Comment, ProcessingInstruction et l'élément racine du document encapsulé dans un objet de type Element.

Les éléments d'un document sont encapsulés dans des classes dédiées : Element, Attribute, Text, ProcessingInstruction, Namespace, Comment, DocType, EntityRef, CDATA.

Un objet de type Element peut contenir des objets de type Comment, Text et d'autres objets de type Element.

A l'exception des objets de type Namespace, les éléments sont créés en utilisant leur constructeur.

JDOM vérifie que les données contenues dans les éléments respectent la norme XML : par exemple, il n'est pas possible de créer un commentaire contenant deux caractères moins qui se suivent.

Une fois un document XML encapsulé dans un arbre d'objets, il est possible de modifier cet arbre dans le respect des spécifications de XML.

JDOM permet d'exporter un arbre d'objets d'un document XML dans un flux, un arbre DOM ou un ensemble d'événements SAX.

JDOM interagit donc avec SAX et DOM pour créer un document en utilisant ces parseurs ou pour exporter un document vers ces API, ce qui permet de facilement intégrer JDOM dans des traitements existants. JDOM propose cependant sa propre API.

 

45.1.3. Les fonctionnalités et les caractéristiques

JDOM propose plusieurs fonctionnalités :

Les points caractéristiques de l'API JDOM sont :

Il est légitime de se demander qu'elle est l'utilité de proposer une nouvelle API pour manipuler des documents XML en Java alors que plusieurs standards existent déjà. En fait le besoin est réel car JDOM propose des réponses à certaines faiblesses de SAX et DOM.

DOM est une API indépendante de tout langage : son implémentation en Java ne tient donc pas compte des spécificités et standards de Java ce qui rend sa mise en oeuvre peu aisée. JDOM est plus intuitif et facile à mettre en oeuvre que DOM.

Comme DOM, JDOM encapsule un document XML entier dans un arbre d'objets. Par contre chaque élément du document est encapsulé dans une classe dédiée selon son type et non sous la forme d'un objet de type Node.

JDOM peut être utilisé comme une alternative à DOM pour manipuler un document XML. JDOM ne remplace pas DOM puisque ce n'est pas un parseur, de plus il propose des interactions avec DOM en entrée et en sortie.

L'utilisation de DOM requiert de nombreuses ressources notamment à cause de son API qui de surcroît n'est pas intuitive en Java. DOM est développé de façon indépendante de tout langage et son organisation est proche de celle des spécifications XML (tous les éléments sont des Nodes par exemple).

SAX est particulièrement bien adapté à la lecture rapide avec peu de ressources d'un document XML mais son modèle de traitement par événements n'est pas intuitif et surtout SAX ne permet pas de modifier ni de naviguer dans un document.

JDOM propose d'apporter une solution à ces différents problèmes dans une seule et même API.

Afin de les rendre plus clairs, la plupart des exemples de ce chapitre héritent de la classe ci-dessous qui propose un moyen pour exporter un document vers la console.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public abstract class TestJDOM {

  protected static void afficher(Document document)
  {
    try
    {
      XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
      sortie.output(document, System.out);
    } catch (java.io.IOException e){}
  }
}

 

45.1.4. L'installation de JDOM

Avant la version 1.0 de JDOM, il était nécessaire de compiler l'API avec un script Ant fourni.

A partir de la version 1.0, l'archive de JDOM contient directement un binaire de la bibliothèque utilisable.

 

45.1.4.1. L'installation de JDOM version 1 bêta 7 sous Windows

Pour utiliser JDOM il faut construire la bibliothèque grâce à l'outil Ant. Ant doit donc être installé sur la machine.

Il faut aussi que la valeur de la variable JAVA_HOME soit définie avec le répertoire qui contient le JDK.

Exemple :
set JAVA_HOME=c:\j2sdk1.4.0-rc

Il suffit alors d'exécuter le fichier build.bat situé dans le répertoire d'installation de JDom.

Un message informe de la fin de la compilation :

Exemple :
package:

[jar] Building jar: C:\java\jdom-b7\build\jdom.jar
BUILD SUCCESSFUL
Total time: 1 minutes 33 seconds

Le fichier jdom.jar est créé dans le répertoire build.

Pour utiliser JDOM dans un projet, il faut obligatoirement avoir un parseur XML SAX et/ou DOM. Pour les exemples de cette section, lorsqu'aucun parser n'est fourni avec le JDK, c'est Xerces qui est utilisé. Il faut aussi avoir le fichier jaxp.jar.

Pour compiler et exécuter les exemples de cette section, j'ai utilisé le script suivant :

Exemple :
javac %1.java -classpath .;jdom.jar;xerces.jar;jaxp.jar
java -classpath .;jdom.jar;xerces.jar;jaxp.jar %1

 

45.1.4.2. L'installation de la version 1.x 

L'archive contenant JDOM peut être téléchargée à l'url http://www.jdom.org/dist/binary/

Il suffit de décompresser le contenu de l'archive dans un répertoire du système et d'ajouter le fichier jdom.jar contenu dans le sous-répertoire build au classpath.

 

45.1.5. Les différentes entités de JDOM

Pour traiter un document XML, JDOM définit plusieurs entités qui peuvent être regroupées en trois groupes :

Ces classes sont regroupées dans cinq packages :

Attention : cette API a énormément évoluée jusqu'à sa version 1.0. Beaucoup de méthodes ont été déclarées deprecated au fur et à mesure des différentes versions bêta.

 

45.1.5.1. La classe Document

La classe org.jdom.Document encapsule l'arbre dans lequel JDOM stocke le document XML. Pour obtenir un objet Document, il y a deux possibilités :

Pour créer un nouveau document, il suffit d'instancier un objet Document en utilisant un des constructeurs fournis dont les principaux sont :

Constructeur

Rôle

Document()

 

Document(Element)

Création d'un document avec l'élément racine fourni

Document(Element, DocType)

Création d'un document avec l'élément racine et la déclaration doctype fournie

Document(List)

Création d'un document avec les entités fournies (élément racine, commentaires, instructions de traitement)

Document(List, DocType)

Création d'un document avec les entités et le type de document fournis


Exemple :
import org.jdom.*;

public class TestJDOM2 {

  public static void main(String[] args) {
    Element racine = new Element("bibliothèque");
    Document document = new Document(racine);
  }       
}

La classe Document possède plusieurs méthodes dont les principales sont :

Méthode

Rôle

Document addContent(Comment)

Ajouter un commentaire au document

List getContent()

Renvoyer un objet List qui contient chaque élément du document

DocType getDocType()

Renvoyer un objet contenant les caractéristiques Doctype du document

Document setDocType()

Définir le DocType du document

Element getRootElement()

Renvoyer l'élément racine du document

Document setRootElement(Element)

Définir l'élément racine du document

boolean hasRootElement()

Renvoyer un booléen qui indique si le document possède un élément racine

Element detachRootElement()

Détache l'élément racine du document

Document addContent(ProcessingInstruction)

Ajouter une instruction de traitement

Document addContent(Comment)

Ajouter un commentaire

Document removeContent(ProcessingInstruction)

Supprimer une instruction de traitement

Document removeContent(Comment)

Supprimer un commentaire


Un objet de type Document possède :

Un document peut ne pas avoir d'élément racine, lorsqu'il est créé avec le constructeur par défaut mais dans ce cas, le document n'est utilisable qu'à partir du moment où l'élément racine lui est ajouté.

Pour obtenir un document à partir d'un document XML existant, JDOM propose les classes SAXBuilder et DOMbuilder du package org.jdom.input.

 

45.1.5.2. La classe DocType

JDOM n'offre pas un modèle complet d'objets pour le support des DTD : seule la classe DocType est proposée pour encapsuler la déclaration d'un type document.

Pour instancier un objet de type DocType, il suffit d'utiliser un de ses constructeurs.

L'association de la DTD au document peut se faire en utilisant le constructeur de la classe Document qui attend un tel objet en paramètre ou en utilisant la méthode setDocType() de la classe Document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.DocType;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM4 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    DocType docType = new DocType("bibliotheque", "bibliotheque.dtd");
    Document document = new Document(racine, docType);
    
    afficher(document);
  }
}

Exemple :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bibliotheque SYSTEM "bibliotheque.dtd">

<bibliotheque />

La classe DocType n'encapsule que des données informatives sur la DTD dans 4 propriétés :

Exemple de déclaration :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Dans cet exemple :

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.DocType;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM6 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("html");
    DocType docType = new DocType("html", "-//W3C//DTD XHTML 1.0 Transitional//EN",
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd");

    Document document = new Document(racine, docType);

    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html />

JDOM permet de déclarer une DTD mais ne l'utilise en aucune façon pour assurer la validité du document. JDOM vérifie simplement que le document est bien formé.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.DocType;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM7 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("test");
    DocType docType = new DocType("html", "-//W3C//DTD XHTML 1.0 Transitional//EN",
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd");

    Document document = new Document(racine, docType);

    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<test />

Pour définir une DTD directement dans la déclaration, il faut utiliser la méthode setInternalSubset() en lui passant en paramètre la chaîne de caractères contenant la DTD.

Remarque : JDOM ne permet pas de valider un document en mémoire.

Pour vérifier la validité d'un document, il faut exporter le document et utiliser un parseur en activant l'option de validation.

 

45.1.5.3. La classe Element

La structure d'un document XML est composée d'éléments encapsulés dans la classe org.jdom.Element.

Un élément peut contenir du texte, des attributs, des commentaires et tous les autres éléments définis par la norme XML.

Cette classe possède plusieurs constructeurs dont les principaux sont :

Constructeur

Rôle

Element(String)

Créer un élément, en précisant son nom

Element(String, Namespace)

Créer un élément, en précisant son nom et son espace de nommage

Element(String, String)

Créer un objet, en précisant son nom et l'URI de son espace de nommage

Element(String, String, String)

Créer un objet, en précisant son nom, le préfixe et l'URI de son espace de nommage


La classe Element possède plusieurs propriétés :

La classe Element possède de nombreuses méthodes pour obtenir, ajouter ou supprimer une entité de l'élément (un élément enfant, le texte, un attribut, un commentaire, ...) :

Méthode

Rôle

Element addContent(Comment)

Ajouter un commentaire à l'élément

Element addContent(Element)

Ajouter un élément fils à l'élément

Element addContent(String text)

Ajouter des données sous forme de texte à l'élément

Attribute getAttribute(String)

Renvoyer l'attribut dont le nom est fourni en paramètre

Attribute getAttribute(String, Namespace)

Renvoyer l'attribut dont le nom et l'espace de nommage sont fournis en paramètres

List getAttributes()

Renvoyer une collection qui contient tous les attributs

String getAttributeValue(String)

Renvoyer la valeur de l'attribut dont le nom est fourni en paramètres

String getAttributeValue(String, Namespace)

Renvoyer la valeur de l'attribut dont le nom et l'espace de nommage sont fournis en paramètres

Element getChild(String)

Renvoyer le premier élément enfant dont le nom est fourni en paramètre

Element getChild(String, Namespace)

Renvoyer le premier élément enfant dont le nom et l'espace de nommage sont fournis en paramètres

List getChildren()

Renvoyer une liste qui contient tous les éléments enfants directement rattachés à l'élément

List getChildren(String)

Renvoyer une liste qui contient tous les éléments enfants directement rattachés à l'élément dont le nom est fourni en paramètre

List getChildren(String, Namespace)

Renvoyer une liste qui contient tous les éléments enfants directement rattachés à l'élément dont le nom et l'espace de nommage sont fournis en paramètre

String getChildText(String name)

Renvoyer le texte du premier élément enfant dont le nom est fourni en paramètre

String getChildText(String, Namespace)

Renvoyer le texte du premier élément enfant dont le nom et l'espace de nommage sont fournis en paramètres

List getContent()

Renvoyer une liste qui contient toutes les entités de l'élément (texte, élément, commentaire ...)

Document getDocument()

Renvoyer l'objet Document qui contient l'élément

Element getParent()

Renvoyer l'élément père de l'élément

String getText()

Renvoyer les données au format texte contenues dans l'élément

boolean hasChildren()

Renvoyer un booléen qui indique si l'élément possède des éléments fils

boolean isRootElement()

Renvoyer un booléen qui indique si l'élément est l'élément racine du document

boolean removeAttribute(String)

Supprimer l'attribut dont le nom est fourni en paramètre

boolean removeAttribute(String, Namespace)

Supprimer l'attribut dont le nom et l'espace de nommage sont fournis en paramètres

boolean removeChild(String)

Supprimer l'élément enfant dont le nom est fourni en paramètre

boolean removeChild(String, Namespace)

Supprimer l'élément enfant dont le nom et l'espace de nommage sont fournis en paramètres

boolean removeChildren()

Supprimer tous les éléments enfants

boolean removeChildren(String)

Supprimer tous les éléments enfants dont le nom est fourni en paramètre

boolean removeChildren(String, Namespace)

Supprimer tous les éléments enfants dont le nom et l'espace de nommage sont fournis en paramètres

boolean removeContent(Comment)

Supprimer le commentaire fourni en paramètre

boolean removeContent(Element)

Supprimer l'élément fourni en paramètre

Element setAttribute(Attribute)

Ajouter un attribut

Element setAttribute(String, String)

Ajouter un attribut dont le nom et la valeur sont fournis en paramètres

Element setAttribute(String, String, Namespace)

Ajouter un attribut dont le nom, la valeur et l'espace de nommage sont fournis en paramètres

Element setText(String)

Mettre à jour les données au format texte de l'élément


Pour obtenir l'élément racine d'un document, il faut utiliser la méthode getRootElement() de la classe Document. Celle-ci renvoie un objet de type Element. Il est ainsi possible d'utiliser les méthodes ci-dessus pour parcourir et modifier le contenu du document.

L'utilisation de la classe Elément est très facile.

Pour créer un élément, il suffit d'instancier un objet de type Element.

Exemple :
          Element element = new Element("element1");
          element.setAttribute("attribut1","valeur1");
          element.setAttribute("attribut2","valeur2");

La classe possède plusieurs méthodes pour obtenir les entités de l'élément, un élément fils particulier ou une liste d'élément fils. Des appels successifs à ces méthodes permettent d'obtenir un élément précis du document.

Les méthodes de type setter, qui devraient par convention ne rien retourner (void), renvoient l'instance de type Element elle-même. Ceci permet de chaîner les appels aux différents setters.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM28 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element livres = new Element("livres");
    racine.addContent(livres);
    livres.addContent(new Element("livre")
      .addContent(new Element("titre").setText("Titre livre 1"))
      .addContent(new Element("auteur").setText("Auteur 1"))
    );
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livres>
    <livre>
      <titre>Titre livre 1</titre>
      <auteur>Auteur 1</auteur>
    </livre>
  </livres>
</bibliotheque>

 

45.1.5.4. La classe Attribut

La classe org.jdom.Attribut encapsule un attribut d'un élément.

La classe Attribut possède plusieurs propriétés :

La classe Element propose les méthodes getAttribute() et getAttributeValue() pour obtenir un attribut ou la valeur d'un attribut sous la forme d'un objet de type String. La méthode getAttribute() renvoie null si l'attribut n'existe pas.

La classe Attribute propose plusieurs méthodes getXXXValue() qui tentent de fournir la valeur de l'attribut dans le type primitif XXX. Si la conversion échoue, alors une exception de type org.jdom.DataConversionException est levée.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM21 extends TestJDOM {

  public static void main(String[] args) {

    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("test.xml"));

      afficher(document);

      Element table = document.getRootElement().getChild("body").getChild("table");
      System.out.println("attribut width : " + table.getAttributeValue("width"));

      System.out.println("");
      Attribute border = table.getAttribute("border");
      System.out.println("attribut " + border.getName() + " : " + border.getValue());
      System.out.println("attribut " + border.getName() + " : " + border.getIntValue());

      System.out.println("");
      Attribute width = table.getAttribute("width");
      try {
        System.out.println("attribut " + width.getName() 
          + " : " + width.getIntValue());
      } catch (DataConversionException dce) {
        System.out.println("attribut " + width.getName() 
          + " : impossible d'obtenir une valeur entière");
      }

      System.out.println("");
      Attribute cellspacing = table.getAttribute("cellspacing");
      if (cellspacing == null) {
        System.out.println("l'attribut cellspacing n'existe pas");
      }
    } catch (JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<html>
  <head />
  <body>
    <table width="100%" border="0" />
  </body>
</html>
 
attribut width : 100%
 
attribut border : 0
attribut border : 0
 
attribut width : impossible d'obtenir une valeur entière
 
l'attribut cellspacing n'existe pas

L'association d'un attribut à un élément ou sa suppression se fait généralement en utilisant les méthodes setAttribute() et removeAttribute() de la classe Element plutôt qu'en manipulant des instances de la classe Attribute.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM22 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("html");
    Document document = new Document(racine);
    Element body = new Element("body");
    racine.addContent(body);
    Element table = new Element("table");
    body.addContent(table);
    table.setAttribute("width","100%");
    table.setAttribute(new Attribute("border","0"));
    table.setAttribute("cellspacing","10");
    table.removeAttribute("cellspacing");
    
    afficher(document);
  }
}

Résultat :
 <?xml version="1.0" encoding="UTF-8"?>
<html>
  <body>
    <table width="100%" border="0" />
  </body>
</html>

JDom vérifie les valeurs fournies pour les propriétés Name et Value.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM24 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("html");
    Document document = new Document(racine);
    Element body = new Element("body");
    racine.addContent(body);
    Element table = new Element("table");
    body.addContent(table);
    table.setAttribute("@valeur","100");
    
    afficher(document);
  }
}

Résultat :
Exception in thread "main" org.jdom.IllegalNameException: 
The name "@valeur" is not legal for JDOM/XML attributes: 
XML names cannot begin with the character "@".
        at org.jdom.Attribute.setName(Attribute.java:363)
        at org.jdom.Attribute.<init>(Attribute.java:227)
        at org.jdom.Attribute.<init>(Attribute.java:251)
        at org.jdom.Element.setAttribute(Element.java:1128)
        at com.jmdoudoux.test.jdom.TestJDOM24.main(TestJDOM24.java:21)

La classe Element propose la méthode getAttributes() qui renvoie une collection d'objets de type Attribute contenant tous les attributs de l'élément.

Pour supprimer tous les attributs d'un élément, il suffit d'utiliser la méthode clear() sur la collection.

Exemple :
package com.jmdoudoux.test.jdom;

import java.util.List;
import java.util.ListIterator;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM23 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("html");
    Document document = new Document(racine);
    Element body = new Element("body");
    racine.addContent(body);
    Element table = new Element("table");
    body.addContent(table);
    table.setAttribute("width","100%");
    table.setAttribute(new Attribute("border","0"));
    table.setAttribute("cellspacing","10");
    
    List attributs = table.getAttributes();
    ListIterator iterator = attributs.listIterator();
    
    System.out.println("Liste des attributs");
    while (iterator.hasNext()) {
      Attribute attribut = (Attribute) iterator.next();
      System.out.println("attribut "+attribut.getName()+" : "+attribut.getValue());
    }
    
    System.out.println();
    // supprimer tous les attributs

    attributs.clear();
    
    afficher(document);
  }
}

Résultat :

Liste des attributs
attribut width : 100%
attribut border : 0
attribut cellspacing : 10

<?xml version="1.0" encoding="UTF-8"?>
<html>
  <body>
    <table />
  </body>
</html>

Un objet de type Attribut ne peut avoir qu'un seul élément parent.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM25 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("html");
    Document document = new Document(racine);
    Element body = new Element("body");
    racine.addContent(body);
    Element table = new Element("table");
    body.addContent(table);
    Attribute attribut = new Attribute("width","100%");
    table.setAttribute(attribut);
    body.setAttribute(attribut);
    
    afficher(document);
  }
}

Résultat :
Exception in thread "main" org.jdom.IllegalAddException: 
The attribute already has an existing parent "table"
        at org.jdom.AttributeList.add(AttributeList.java:187)
        at org.jdom.AttributeList.add(AttributeList.java:131)
        at org.jdom.Element.setAttribute(Element.java:1181)
        at com.jmdoudoux.test.jdom.TestJDOM25.main(TestJDOM25.java:23)

Pour déplacer un attribut vers un autre élément, il faut au préalable utiliser la méthode detach().

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM26 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("html");
    Document document = new Document(racine);
    Element body = new Element("body");
    racine.addContent(body);
    Element table = new Element("table");
    body.addContent(table);
    Attribute attribut = new Attribute("width","100%");
    table.setAttribute(attribut);
    attribut.detach();
    body.setAttribute(attribut);
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<html>
  <body width="100%">
    <table />
  </body>
</html>

 

45.1.5.5. La classe Text

JDOM encapsule un noeud de type texte du document XML dans la classe Text.

Généralement, cette classe n'est pas utilisée directement car JDOM effectue directement les conversions vers String sauf lors du parcours des éléments fils retournés par la méthode getContent().

Exemple :
package com.jmdoudoux.test.jdom;

import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public class TestJDOM15 {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element adresse = new Element("adresse");
    adresse.addContent("mon adresse");
    racine.addContent(adresse);
    
    List elements = adresse.getContent();
    for (Object element : elements) {
      System.out.println(element.getClass().getName());
    }
  }
}

Résultat :
org.jdom.Text

La classe Texte stocke les caractères dans un champ value auquel il est possible d'accéder par la propriété Text (getText() et setText()).

Il n'est pas utile d'échapper les caractères utilisés par XML( <, >, &, ...). Il suffit de les fournir tels quels et JDOM les échappera lors de l'exportation du document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM16 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element adresse = new Element("adresse");
    adresse.addContent("mon adresse < 5 et > 10 & impaire ");
    racine.addContent(adresse);
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <adresse>mon adresse &lt; 5 et &gt; 10 &amp; impaire</adresse>
</bibliotheque>

La classe Text possède de nombreuses méthodes

Méthode

Rôle

String getText()

Obtenir la valeur du texte

String getTextTrim()

 

String getTextNormalize()

 

String normalizeString(String)

 

Text setText(String)

Modifier la valeur du texte

void append(String)

Ajouter la chaîne de caractères à la valeur du texte

void append(Text)

Ajoute la valeur du texte de l'objet fourni à la valeur du texte

Element getParent();

Obtenir l'élément qui contient le texte

Document getDocument()

Obtenir le document qui contient le texte

Text setParent(Element)

Associer le texte à son élément parent

Text detach()

Détacher le texte de son élément père


La classe Text possède plusieurs propriétés :

JDOM ne garantit pas que le contenu textuel d'un élément soit stocké dans un unique objet Text.

 

45.1.5.6. La classe Comment

La classe org.jdom.Comment encapsule un commentaire dans le document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM18 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Comment comment1 = new Comment("mon commentaire bibliotheque");
    racine.addContent(comment1);
    Document document = new Document(racine);
    Element adresse = new Element("adresse");
    Comment comment2 = new Comment("mon commentaire adresse");
    adresse.addContent(comment2);
    adresse.addContent("mon adresse");

    racine.addContent(adresse);
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <!--mon commentaire bibliotheque-->
  <adresse>
    <!--mon commentaire adresse-->
    mon adresse
  </adresse>
</bibliotheque>

Il est possible d'ajouter un commentaire directement au document :

Exemple :
package com.jmdoudoux.test.jdom;

import java.util.List;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM19 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    document.addContent(new Comment("mon commentaire de fin"));
    Element adresse = new Element("adresse");
    racine.addContent(adresse);
    
    Comment comment = new Comment("mon commentaire de debut");
    List content = document.getContent();
    content.add(0, comment); 
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<!--mon commentaire de debut-->
<bibliotheque>
  <adresse />
</bibliotheque>
<!--mon commentaire de fin-->

Lors de l'instanciation d'un objet de type Comment, l'API vérifie que le contenu du commentaire respecte les spécifications XML et lève une exception de type IllegalDataException si celles-ci ne sont pas respectées.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM20 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Comment comment1 = new Comment("mon commentaire -- bibliotheque");
    racine.addContent(comment1);
    Document document = new Document(racine);
    Element adresse = new Element("adresse");
    adresse.addContent("mon adresse");
    racine.addContent(adresse);
        
    afficher(document);
  }
}

Exemple :
Exception in thread "main" org.jdom.IllegalDataException:
The data "mon commentaire -- bibliotheque" 
is not legal for a JDOM comment: Comments cannot contain double hyphens (--).
        at org.jdom.Comment.setText(Comment.java:120)
        at org.jdom.Comment.<init>(Comment.java:86)
        at com.jmdoudoux.test.jdom.TestJDOM20.main(TestJDOM20.java:13)

 

45.1.5.7. La classe Namespace

La classe org.jdom.Namespace encapsule un espace de nommage associé à un élément ou à un attribut. Chaque Namespace possède un URI. Si l'espace de nommage n'est pas celui par défaut, alors il doit avoir un préfixe sinon le préfixe est une chaîne vide.

Il peut y avoir autant d'espaces de nommages que nécessaire dans un même document.

Pour limiter l'occupation mémoire requise par l'espace de nommage de chaque éléments, il n'existe qu'une seule instance de la classe Namespace pour un même espace de nommage. Ceci est garantit par le fait que la classe Namespace ne possède pas de constructeur accessible et fait office de fabrique pour ces instances.

La méthode statique getNamespace() permet de retrouver ou de créer un espace de nom : elle attend en paramètre une URI et/ou un préfixe. Elle permet d'assurer que son appel avec les mêmes paramètres renvoie systématiquement la même instance de la classe Namespace.

Exemple :
      Namespace ns1 = Namespace.getNamespace("http://www.jmdoudoux.com");
      Namespace ns2 = Namespace.getNamespace("bibliotheque", "http://www.jmdoudoux.com");

Un objet de type Namespace peut être passé en paramètre du constructeur des classes Element et Attribute.

La surcharge de la méthode getNamespace() qui attend uniquement l'uri en paramètres permet de définir un espace de nommage par défaut.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;

public class TestJDOM71 extends TestJDOM {

  public static void main(String[] args) {

    Namespace ns = Namespace.getNamespace("http://www.jmdoudoux.com");

    Element racine = new Element("bibliotheque", ns);
    Document document = new Document(racine);

    Element livre = new Element("livre", ns);
    racine.addContent(livre);

    livre.addContent(new Element("titre", ns).setText("titre1"));

    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque xmlns="http://www.jmdoudoux.com">
  <livre>
    <titre>titre1</titre>
  </livre>
</bibliotheque>

La surcharge de la méthode getNamespace() qui attend le préfixe et l'uri en paramètre permet de définir un espace de nommage possédant un préfixe.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;

public class TestJDOM72 extends TestJDOM {

  public static void main(String[] args) {

    Namespace ns = Namespace.getNamespace("bibliotheque","http://www.jmdoudoux.com");

    Element racine = new Element("bibliotheque", ns);
    Document document = new Document(racine);

    Element livre = new Element("livre", ns);
    racine.addContent(livre);

    livre.addContent(new Element("titre", ns).setText("titre1"));

    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque:bibliotheque xmlns:bibliotheque="http://www.jmdoudoux.com">
  <bibliotheque:livre>
    <bibliotheque:titre>titre1</bibliotheque:titre>
  </bibliotheque:livre>
</bibliotheque:bibliotheque>

Il n'est pas possible de créer un élément dont le nom contient un caractère « : » : celui-ci est réservé par les spécifications de XML pour préciser un espace de nommage.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;

public class TestJDOM73 extends TestJDOM {

  public static void main(String[] args) {

    Namespace ns = Namespace.getNamespace("bibliotheque","http://www.jmdoudoux.com");

    Element racine = new Element("bibliotheque", ns);
    Document document = new Document(racine);

    Element livre = new Element("bibliotheque:livre");
    racine.addContent(livre);

    livre.addContent(new Element("titre", ns).setText("titre1"));

    afficher(document);
  }
}

Résultat :
Exception in thread "main" org.jdom.IllegalNameException: The name "bibliotheque:livre" 
is not legal for JDOM/XML elements: Element names cannot contain colons.
        at org.jdom.Element.setName(Element.java:207)
        at org.jdom.Element.<init>(Element.java:141)
        at org.jdom.Element.<init>(Element.java:153)
        at com.jmdoudoux.test.jdom.TestJDOM73.main(TestJDOM73.java:17)

Il faut utiliser une des surcharges du constructeur de la classe Element pour fournir l'espace de nommage.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;

public class TestJDOM74 extends TestJDOM {

  public static void main(String[] args) {

    Namespace ns = Namespace.getNamespace("bibliotheque","http://www.jmdoudoux.com");

    Element racine = new Element("bibliotheque", ns);
    Document document = new Document(racine);

    Element livre = new Element("livre", "bibliotheque", "http://www.jmdoudoux.com");
    racine.addContent(livre);

    livre.addContent(new Element("titre", ns).setText("titre1"));

    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque:bibliotheque xmlns:bibliotheque="http://www.jmdoudoux.com">
  <bibliotheque:livre>
    <bibliotheque:titre>titre1</bibliotheque:titre>
  </bibliotheque:livre>
</bibliotheque:bibliotheque>

Comme chaque objet de type Element et Attribute possède une référence sur un objet de type Namespace, il n'y a pas besoin de se préoccuper de l'espace de nommage lors du déplacement d'un élément.

Pour obtenir des éléments fils appartenant à un espace de nommage en utilisant les méthodes getChild() et getChildren() de la classe Element, il faut obligatoirement utiliser la surcharge de ces méthodes qui attend en paramètre un objet de type Namespace.

 

45.1.5.8. La classe CData

La classe CDATA est une sous-classe de la classe Text. La grande différence entre ces deux classes est la façon dont leurs données seront exportées par un objet de type XMLOutputter.

Ces données sont incluses dans une section CDATA et les caractères spéciaux qu'elles contiennent ne sont pas échappés.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.CDATA;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM17 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element adresse = new Element("adresse");
    adresse.addContent("mon adresse < 5 et > 10 & impaire ");
    racine.addContent(adresse);
    CDATA cData = new CDATA("mon adresse < 5 et > 10 & impaire ");
    racine.addContent(cData);
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <adresse>mon adresse &lt; 5 et &gt; 10 &amp; impaire</adresse>
  <![CDATA[mon adresse < 5 et > 10 & impaire]]>
</bibliotheque>

 

45.1.5.9. La classe ProcessingInstruction

La classe ProcessingInstruction encapsule une instruction de traitement du document XML. Ces instructions permettent de fournir des informations aux outils qui traitent le document XML.

Une instruction est composée d'une cible (target) et de données (data).

La classe ProcessingInstruction possède plusieurs propriétés :

Lors de l'instanciation d'un objet de type ProcessingInstruction, l'API vérifie que le nom de la cible respecte les spécifications XML et lève une exception de type IllegalTargetException si celles-ci ne sont pas respectées.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.ProcessingInstruction;

public class TestJDOM14 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("html");
    Document document = new Document(racine);
    Element body = new Element("body");
    ProcessingInstruction pi = new ProcessingInstruction("php","echo 'bonjour';");
    body.addContent(pi);
    racine.addContent(body);
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<html>
  <body>
    <?php echo 'bonjour';?>
  </body>
</html>

Comme il est fréquent qu'une instruction de traitement possède des attributs, une surcharge du constructeur attend en paramètre le nom de la cible et un objet de type Map qui encapsule les attributs sous la forme clé/valeur.

Exemple :
package com.jmdoudoux.test.jdom;

import java.util.HashMap;
import java.util.Map;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.ProcessingInstruction;

public class TestJDOM45 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    
    Map instructions = new HashMap();
    instructions.put("href", "bibliotheque.xsl");
    instructions.put("type", "text/xsl");
    ProcessingInstruction pi = new ProcessingInstruction("xml-stylesheet", instructions);
    document.getContent().add(0, pi);
    
    Element adresse = new Element("adresse");
    adresse.addContent("mon adresse");
    racine.addContent(adresse);
    
    afficher(document);
  }
}

Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="application/xml" href="bibliotheque.xsl"?>
<bibliotheque>
  <adresse>mon adresse</adresse>
</bibliotheque>

La méthode getParent() permet de connaître l'Element associé à l'instruction. Cette méthode renvoie null si l'instruction est rattachée directement au document ou si elle n'est pas encore attachée.

La classe ProcessingInstruction propose la méthode getData() qui renvoie toutes les données sous la forme d'une chaîne de caractères.

Fréquemment les données sont sous la forme d'attributs donc la classe ProcessingInstrution propose des méthodes pour faciliter leur manipulation :

Pour obtenir une instruction de traitement d'un élément, il faut utiliser la méthode getContent() de la classe Element ou Document et parcourir la collection pour obtenir celles de type ProcessingInstruction.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.IOException;
import java.io.StringReader;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.ProcessingInstruction;
import org.jdom.input.SAXBuilder;

public class TestJDOM45 extends TestJDOM {

  public static void main(String[] args) {

    StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
    sb.append("<?xml-stylesheet type=\"text/xsl\" href=\"bibliotheque.xsl\"?>");
    sb.append("<bibliotheque>");
    sb.append("  <adresse>mon adresse</adresse>");
    sb.append("</bibliotheque>");

    SAXBuilder builder = new SAXBuilder();
    Document document;
    try {
      document = builder.build(new StringReader(sb.toString()));
      
      List elements = document.getContent();
      Iterator iterator = elements.iterator();
      while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof ProcessingInstruction) {
          ProcessingInstruction pi = (ProcessingInstruction) o;
          System.out.println("PI : "+pi.getTarget());
          
          List names = pi.getPseudoAttributeNames();
          Iterator itNames = names.iterator();
          while (itNames.hasNext()) {
            String name = itNames.next().toString();
            System.out.println("   "+name+ " = "+pi.getPseudoAttributeValue(name));
          }
        }
      }
    } catch (JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Résultat :
PI : xml-stylesheet
   type = text/xsl
   href = bibliotheque.xsl

 

45.1.6. La création d'un document

JDOM encapsule un document XML dans une arborescence d'objets dont le point d'entrée est un objet de type org.jdom.Document.

La création d'un nouveau document se fait simplement en instanciant un objet de type Document grâce à l'opérateur new sur un des constructeurs de la classe.

Une instance de la classe Document peut aussi facilement être créée à partir d'un document XML existant en utilisant les classes SAXBuilder ou DOMBuilder du package org.jdom.input.

 

45.1.6.1. La création d'un nouveau document

Le plus simple pour créer un nouveau document est d'instancier un objet de type Element qui va encapsuler la racine du document et de passer cette instance au constructeur de la classe Document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM48 {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
  }
}

JDOM est beaucoup plus simple que DOM : la création d'un nouveau document avec Dom est plus verbeuse.

Exemple :
package com.jmdoudoux.test.jdom;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class TestJDOM47 {

  public static void main(String[] args) {

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder;
    try {
      builder = factory.newDocumentBuilder();
      DOMImplementation impl = builder.getDOMImplementation();
      Document doc = impl.createDocument(null, null, null);
      Element element = doc.createElement("bibliotheque");
    } catch (ParserConfigurationException e) {
      e.printStackTrace();
    }
  }
}

Il est aussi possible d'instancier un objet de type Document avec le constructeur par défaut et de lui associer l'élément racine en utilisant la méthode setRootElement().

Exemple :
    Document document = new Document();
    Element racine = new Element("bibliotheque");
    document.setRootElement(racine) ;

Dans ce cas, le document débute son existence dans un état illégal : il n'est pas possible de faire des opérations sur le document tant que sa racine n'est pas précisée sinon une exception de type IllegalStateException est levée.

 

45.1.6.2. L'obtention d'une instance de Document à partir d'un document XML

Pour obtenir un objet de type Document à partir d'un document XML existant, JDOM propose deux classes regroupées dans le package org.jdom.input qui implémentent l'interface Builder.

Cette interface définit la méthode build() qui renvoie un objet de type Document et qui est surchargée pour utiliser plusieurs sources différentes : flux (InputStream, Reader) , fichier (File) et URL (URL et String).

Les deux classes sont SAXBuilder et DOMBuilder.

JDOM ne fournit aucun parseur XML : il utilise celui précisé par JAXP ou celui explicitement demandé.

 

45.1.6.2.1. L'obtention d'une instance de Document à partir d'un document XML en utilisant SAX

La classe SAXBuilder permet d'analyser le document XML avec un parseur de type SAX compatible JAXP, de créer un arbre JDOM et de renvoyer un objet de type Document.

La mise en oeuvre de la classe SAXBuilder est très simple et ne nécessite que deux étapes  :

Si l'opération réussie, la méthode build() retourne un objet de type Document encapsulant le document sinon elle lève une exception de type JDOMException quand l'analyse du document échoue ou une exception de type IOException en cas de problème lors de la lecture du document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.*;
import org.jdom.input.*;
import java.io.*;

public class TestJDOM3 {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("test.xml"));
    } catch (JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

L'exception JDOMException est la classe mère de plusieurs exceptions notamment JDOMParseException qui permet de signifier un problème dans les traitements de JDOM.

Exemple avec le document XML :
<?xml version="1.0" encoding="UTF-8"?>
<html>
  <head></head>
  <body>
    <table width="100%" border="0"></table>
    <test>
  </body>
</html>
Résultat :
org.jdom.input.JDOMParseException: Error on line 7 of document 
file:/C:/Documents%20and%20Settings/jmd/workspace/TestJDOM/test.xml: 
The element type "test" must be terminated by the matching end-tag "</test>".
      at org.jdom.input.SAXBuilder.build(SAXBuilder.java:501)
      at org.jdom.input.SAXBuilder.build(SAXBuilder.java:847)
      at org.jdom.input.SAXBuilder.build(SAXBuilder.java:826)
      at com.jmdoudoux.test.jdom.TestJDOM3.main(TestJDOM3.java:10)
Caused by: org.xml.sax.SAXParseException: The element type "test" 
must be terminated by the matching end-tag "</test>".
      at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException
      (ErrorHandlerWrapper.java:195)
      at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError
      (ErrorHandlerWrapper.java:174)
      at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError
      (XMLErrorReporter.java:388)
      at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError
      (XMLScanner.java:1411)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement
      (XMLDocumentFragmentScannerImpl.java:1739)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl
      $FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2923)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next
      (XMLDocumentScannerImpl.java:645)
      at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next
      (XMLNSDocumentScannerImpl.java:140)
      at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument
      (XMLDocumentFragmentScannerImpl.java:508)
      at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse
      (XML11Configuration.java:807)
      at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse
      (XML11Configuration.java:737)
      at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:107)
      at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse
      (AbstractSAXParser.java:1205)
      at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse
      (SAXParserImpl.java:522)
      at org.jdom.input.SAXBuilder.build(SAXBuilder.java:489)
      ... 3 more

Par défaut, le parseur utilisé par JDOM est celui précisé par JAXP ou à défaut Xerces.

SAXBuilder possède plusieurs constructeurs qui permettent de préciser la classe du parseur (nom de la classe pleinement qualifiée de type XMLReader) à utiliser et/ou un booléen qui indique si le document doit être validé.

Par défaut, les vérifications faites par SAXBuilder sur le document XML ne concernent que le fait que le document soit bien formé. Pour valider le document explicitement, il faut demander la validation en passant la valeur true au paramètre validate du constructeur de SAXBuilder.

Exemple :
package com.jmdoudoux.test.jdom;
      
import org.jdom.*;
import org.jdom.input.*;
import java.io.*;
        
public class TestJDOM3 {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder(true);
      Document document = builder.build(new File("test.xml"));
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
org.jdom.input.JDOMParseException: Error on line 2 of document 
file:/C:/Documents%20and%20Settings/jmd/workspace/TestJDOM/test.xml: 
Document is invalid: no grammar found.
        at org.jdom.input.SAXBuilder.build(SAXBuilder.java:501)
        at org.jdom.input.SAXBuilder.build(SAXBuilder.java:847)
        at org.jdom.input.SAXBuilder.build(SAXBuilder.java:826)
        at com.jmdoudoux.test.jdom.TestJDOM3.main(TestJDOM3.java:10)
Caused by: org.xml.sax.SAXParseException: Document is invalid: no grammar found.
        at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException
        (ErrorHandlerWrapper.java:195)
        at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error
        (ErrorHandlerWrapper.java:131)
        at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError
        (XMLErrorReporter.java:384)
        at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError
        (XMLErrorReporter.java:318)
...

Dans l'exemple ci-dessus, la validation échoue car aucune DTD n'est précisée dans le document.

Il est possible de configurer le parseur SAX utilisé par SAXBuilder grâce à plusieurs méthodes :

Il est possible de construire un arbre JDOM en utilisant la classe SAXBuilder à partir d'une chaîne de caractères contenant le document XML. Il suffit d'instancier un objet de type StringReader() en lui passant en paramètre la chaîne de caractères contenant le document.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.IOException;
import java.io.StringReader;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM49 {

  public static void main(String[] args) {
    try {
      StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
      sb.append("<?xml-stylesheet type=\"text/xsl\" href=\"bibliotheque.xsl\"?>");
      sb.append("<bibliotheque>");
      sb.append("  <adresse>mon adresse complete</adresse>");
      sb.append("</bibliotheque>");

      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new StringReader(sb.toString()));
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}

L'utilisation de SAXBuilder est particulièrement recommandée car elle consomme moins de ressources.

 

45.1.6.2.2. L'obtention d'une instance de Document à partir d'un arbre DOM

Bien qu'ayant des similitudes de nom, JDOM n'est pas compatible avec DOM. Il n'est pas possible d'utiliser des éléments d'une API dans l'autre directement.

JDOM propose cependant de convertir un arbre DOM en modèle JDOM et vice versa. Ceci est pratique pour intégrer JDOM dans du code existant manipulant des arbres DOM.

Pour créer un modèle JDOM à partir d'un arbre DOM, il faut utiliser la classe org.jdom.input.DomBuilder. Sa mise en oeuvre est similaire à celle de la classe SAXBuidler : instancier un objet de type DOMBuilder et invoquer sa méthode build().

La classe DOMBuilder propose deux surcharges de la méthode build() :

Les modifications faites dans le modèle JDOM n'ont aucun impact sur l'arbre DOM utilisé initialement pour créer l'arbre d'objets JDOM et vice versa.

 

45.1.6.3. La création d'éléments

Pour créer un nouvel élément, il suffit d'instancier un objet de la classe Element. Tous les constructeurs de cette classe attendent au moins en paramètre le nom de l'élément. Ce nom doit respecter les spécifications XML, sinon une exception de type IllegalNameException est levée.

L'exception IllegalNameException hérite de l'exception IllegalArgumentException qui est une exception de type runtime : il n'est donc pas obligatoire de traiter ce type d'exception mais elle peut tout de même être levée à l'exécution.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM29 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element livres = new Element("*livres");
    racine.addContent(livres);
    
    afficher(document);
  }
}
Résultat :
Exception in thread "main" org.jdom.IllegalNameException: 
The name "*livres" is not legal for JDOM/XML elements: 
XML names cannot begin with the character "*".
        at org.jdom.Element.setName(Element.java:207)
        at org.jdom.Element.<init>(Element.java:141)
        at org.jdom.Element.<init>(Element.java:153)
        at com.jmdoudoux.test.jdom.TestJDOM29.main(TestJDOM29.java:13)

Chaque Element peut avoir autant d'objets de type Element fils que nécessaire pour représenter le document XML.

Il est possible de préciser le texte associé à l'élément en utilisant la méthode setText() de la classe Element.

 

45.1.6.4. L'ajout d'éléments fils

Pour ajouter un élément fils à un élément, il faut instancier l'élément fils et utiliser la méthode addContent() de l'instance de l'objet père.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM30 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element livres = new Element("livres");
    racine.addContent(livres);
    
    Element livre = new Element("livre");
    livres.addContent(livre);
    
    Element titre = new Element("titre").setText("Titre livre 1");
    Element auteur = new Element("auteur").setText("Auteur 1");
    livre.addContent(titre);
    livre.addContent(auteur);
    
    afficher(document);
  }
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livres>
    <livre>
      <titre>Titre livre 1</titre>
      <auteur>Auteur 1</auteur>
    </livre>
  </livres>
</bibliotheque>

Comme la plupart des méthodes qui modifient un Element renvoient l'élément lui-même, il est possible de chaîner les différents appels aux méthodes.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM28 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element livres = new Element("livres");
    racine.addContent(livres);
    livres.addContent(new Element("livre")
      .addContent(new Element("titre").setText("Titre livre 1"))
      .addContent(new Element("auteur").setText("Auteur 1"))
    );
    
    afficher(document);
  }
}

Attention : la lisibilité du code devient moins triviale.

Il est possible de définir sa propre classe qui hérite de la classe Element pour par exemple créer une portion de document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Element;

public class ElementLivre extends Element {

  private static final long serialVersionUID = 1L;

  public ElementLivre(String titre, String auteur) {
    super("Livre");
    addContent(new Element("titre").setText(titre));
    addContent(new Element("auteur").setText(auteur));
  }
}

Il suffit alors d'utiliser cette classe pour créer le document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM31 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element livres = new Element("livres");
    racine.addContent(livres);
    
    Element livre = new ElementLivre("Titre livre 1","Auteur 1");
    livres.addContent(livre);
    
    afficher(document);
  }
}

 

45.1.7. L'arborescence d'éléments

Un document XML possède une structure arborescente dans laquelle un Element peut avoir des Elements fils.

Cette section va utiliser le document xml suivant

Exemple : le fichier bibliotheque.xml
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <auteur>auteur2</auteur>
    <editeur>editeur2</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <auteur>auteur3</auteur>
    <editeur>editeur3</editeur>
  </livre>
</bibliotheque>

 

45.1.7.1. Le parcours des éléments

Le parcours des éléments avec JDOM utilise le framework Collection notamment les classes List et Iterator.

La méthode getChildren() de la classe Element renvoie une collection qui encapsule les éléments fils uniquement de premier niveau. Elle possède plusieurs surcharges :

Méthode

Rôle

List getChildren()

Renvoyer tous les éléments fils de premier niveau

List getChildren(String)

Renvoyer tous les éléments fils de premier niveau dont le nom est fourni en paramètre

List getChildren(String, Namescape)

Renvoyer tous les éléments fils de premier niveau dont le nom et l'espace de nommage sont fournis en paramètres


Il suffit alors de réaliser une itération sur la collection pour effectuer les traitements voulus.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.ListIterator;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM5 {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));

      Element element = document.getRootElement();
      List livres = element.getChildren("livre");
      ListIterator iterator = livres.listIterator();
      while (iterator.hasNext()) {
        Element el = (Element) iterator.next();
        System.out.println("titre = " + el.getChild("titre").getText());
      }
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
titre = titre1
titre = titre2
titre = titre3

L'inconvénient d'utiliser un nom de tag pour rechercher un élément est qu'il faut être sûr que l'élément existe sinon une exception de type NullPointerException est levée.

Le plus simple est d'avoir une DTD et d'activer la validation du document par le parseur.

Attention : le nom des éléments utilisés est sensible à la casse.

JDOM n'utilise pas encore les generics : il est donc nécessaire de réaliser des casts et des tests de type sur les éléments des collections.

 

45.1.7.2. L'accès direct à un élément fils

Plutôt que d'itérer sur les éléments fils, il est plus rapide d'utiliser la méthode getChild() de la classe Element surtout s'il n'y a qu'un seul élément fils ou que c'est le premier que l'on souhaite obtenir.

La méthode getChild() possède deux surcharges :

Méthode 

Rôle

Element getChild(String)

Renvoyer le premier fils dont le nom est fourni en paramètre

Element getChild(String, Namespace)

Renvoyer le premier fils dont le nom et l'espace de nommage sont fournis en paramètres


Si aucun fils ne correspond alors la méthode renvoie null.

Si plusieurs fils correspondent alors la méthode getChild() renvoie uniquement le premier.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM41 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Element elementRacine = document.getRootElement();
      Element elementLivre = elementRacine.getChild("livre");
      Element elementAuteur = elementLivre.getChild("auteur");
      System.out.println("auteur du premier livre = "+elementAuteur.getText());      
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
auteur du premier livre = auteur1

Il est possible de chaîner les appels à la méthode getChild() puisqu'elle renvoie l'élément correspondant.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM42 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Element elementAuteur = document.getRootElement().getChild("livre").getChild("auteur");
      System.out.println("auteur du premier livre = " + elementAuteur.getText());
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}

La méthode getChild() ne renvoie que le premier fils correspondant au critère fourni : si plusieurs éléments fils répondent au critère, il faut utiliser la méthode getChildren() et itérer sur la collection.

Si aucun élément fils ne répond au critère alors la méthode getChild() renvoie null ce qui impliquera la levée d'une exception de type NullPointerException.

Pour éviter ceci, il est nécessaire de connaître la structure du document XML et que celui-ci soit validé grâce à une DTD ou un schéma.

Attention, si un élément père contient la définition d'un espace de nommage, il est nécessaire de le préciser.

Exemple : le document XML utilisé
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque xmlns="http://www.jmdoudoux.com">>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <auteur>auteur2</auteur>
    <editeur>editeur2</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <auteur>auteur3</auteur>
    <editeur>editeur3</editeur>
  </livre>
</bibliotheque>

Dans cette version du document XML, la balise racine définit un espace de nommage qui est donc propagé à tous ses éléments fils.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM44 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Element elementRacine = document.getRootElement();
      Element elementLivre = elementRacine.getChild("livre");
      Element elementAuteur = elementLivre.getChild("auteur");
      System.out.println("auteur du premier livre = "+elementAuteur.getText());
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
Exception in thread "main" java.lang.NullPointerException
        at com.jmdoudoux.test.jdom.TestJDOM44.main(TestJDOM44.java:21)

Dans cet exemple, l'objet elementLivre est null car la méthode getChild() renvoie null : il n'existe pas dans le document d'élément fils de l'élément racine avec le nom « livre » et l'espace de nommage par défaut.

Pour que l'exemple précédent fonctionne, il est nécessaire de préciser l'espace de nommage en paramètre de la méthode getChild()

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;

public class TestJDOM44 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Namespace ns = Namespace.getNamespace("http://www.jmdoudoux.com");
      
      Element elementRacine = document.getRootElement();
      Element elementLivre = elementRacine.getChild("livre", ns);
      Element elementAuteur = elementLivre.getChild("auteur", ns);
      System.out.println("auteur du premier livre = "+elementAuteur.getText());
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}

L'API Collection permet aussi d'accéder directement à un des éléments fils.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM43 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Element elementLivre = (Element) document.getRootElement().getChildren("livre").get(2);
      Element elementAuteur = elementLivre.getChild("auteur");
      System.out.println("auteur du troisieme livre = " + elementAuteur.getText());
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
auteur du troisieme livre = auteur3

 

45.1.7.3. Le parcours de toute l'arborescence d'un document

JDOM ne fournit aucune API permettant de parcourir facilement tout le document comme peut le proposer DOM. Il faut utiliser une méthode récursive.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM32 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Element elementRacine = document.getRootElement();
      afficherFils(elementRacine, 0);
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
  
  /**
   * Methode recursive qui parcours un element et affiche ses fils
   * @param element
   * @param niveau
   */
  private static void afficherFils(Element element, int niveau) {
    String indentation = getIndentation(niveau);
    StringBuilder ligne = new StringBuilder(indentation);
    
    ligne.append(element.getName());
    if(element.getChildren().isEmpty()) {
      ligne.append(" = ");
      ligne.append(element.getText());
    }
    
    System.out.println(ligne.toString());
    
    List fils = element.getChildren();
    Iterator iterator = fils.iterator();
    while (iterator.hasNext()) {
      Element elementFils = (Element) iterator.next();
      afficherFils(elementFils, niveau+1);
    } 
  }
  
  private static String getIndentation(int n) {
    char[] car = new char[n];
    Arrays.fill(car, 0, n, ' ');
    return new String(car);
  }
}
Résultat :

bibliotheque
 livre
  titre = titre1
  auteur = auteur1
  editeur = editeur1
 livre
  titre = titre2
  auteur = auteur2
  editeur = editeur2
 livre
  titre = titre3
  auteur = auteur3
  editeur = editeur3

La méthode getChildren() ne renvoie que des Elements. Pour obtenir les autres entités liées à l'élément, il faut utiliser la méthode getContent() qui renvoie toutes les entités (commentaires, texte, ...) y compris les éléments fils sous la forme d'une collection.

Pour traiter chaque élément de cette collection, il est nécessaire de faire un test de type sur l'occurrence de la collection en cours de traitement grâce à l'opérateur instanceof.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM33 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Element elementRacine = document.getRootElement();
      afficherFils(elementRacine, 0);
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
  
  /**
   * Methode recursive qui parcours un element et affiche ses fils
   * @param element
   * @param niveau
   */
  private static void afficherFils(Element element, int niveau) {
    String indentation = getIndentation(niveau);
    StringBuilder ligne = new StringBuilder(indentation);
    
    ligne.append(element.getName());
    if(element.getChildren().isEmpty()) {
      ligne.append(" = ");
      ligne.append(element.getText());
    }
    
    System.out.println(ligne.toString());
    
    List fils = element.getContent();
    Iterator iterator = fils.iterator();
    while (iterator.hasNext()) {
      Object objetFils = iterator.next();
      if (objetFils instanceof Element) {
        Element elementFils = (Element) objetFils;
        afficherFils(elementFils, niveau+1);
      } else {
        if (objetFils instanceof Comment) {
          Comment com = (Comment) objetFils;
          System.out.println(indentation+" -- "+com.getValue());
        }
      }
    }    
  }
  
  private static String getIndentation(int n) {
    char[] car = new char[n];
    Arrays.fill(car, 0, n, ' ');
    return new String(car);
  }
}
Résultat :
bibliotheque
 livre
  --  commentaires livre 1 
  titre = titre1
  auteur = auteur1
  editeur = editeur1
 livre
  --  commentaires livre 2 
  titre = titre2
  auteur = auteur2
  editeur = editeur2
 livre
  --  commentaires livre 3 
  titre = titre3
  auteur = auteur3
  editeur = editeur3

 

45.1.7.4. Les éléments parents

JDOM permet une navigation descendante de l'arbre des objets mais aussi ascendante.

Chaque élément à un unique élément père sauf l'élément racine qui n'en a pas.

La méthode getParent() renvoie l'élément parent de l'élément courant. Elle renvoie null pour l'élément racine du document.

Il est ainsi possible de remonter récursivement dans l'arborescence de niveau en niveau jusqu'à ce qu'il n'y ait plus d'élément parent.

Remarque : un élément qui n'est pas encore rattaché à un document ne possède pas non plus d'élément père : dans ce cas les méthodes getParent() et getDocument() renvoient null.

La classe Element propose la méthode isRootElement() qui renvoie true si l'élément est la racine du document.

La classe Element propose aussi la méthode isAncestor() qui renvoie un booléen indiquant si l'élément est un ancêtre de l'élément fourni en paramètre.

 

45.1.8. La modification d'un document

La classe Element propose de nombreuses méthodes pour modifier un élément :

Méthode

Rôle

addContent()

Ajouter l'entité fournie en paramètre en tant que fils de l'élément. Plusieurs surcharges existent pour des paramètres de type Content, Collection ou String et certaines permettent de définir l'index

removeAttribute()

Supprimer un attribut de l'élément

removeChild()

Supprimer un élément fils

removeChildren()

Supprimer tous les éléments fils dont les noms sont fournis en paramètres

removeContent()

Supprimer une entité fille

setAttribute()

Créer ou modifier un attribut

setContent()

Remplacer un noeud ou tous les noeuds de l'élément

setName()

Modifier le nom de l'élément

setText()

Modifier le texte de l'élément


Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
        
public class TestJDOM66 extends TestJDOM {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));
      
      Element livre2 = (Element) document.getRootElement().getChildren().get(1);
      
      // Ajouter un nouvel élément

      livre2.addContent(new Element("publication").setText("1996"));
      // Supprimer tous les noeuds nommé editeur

      livre2.removeChildren("editeur");
   
      afficher(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <auteur>auteur2</auteur>
    <publication>1996</publication>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <auteur>auteur3</auteur>
    <editeur>editeur3</editeur>
  </livre>
</bibliotheque>

L'appel à la méthode getChildren() renvoie une collection des noeuds fils de l'élément courant. Cette collection est dynamique et peut être directement modifiée pour ajouter/enlever des noeuds, réordonner les noeuds simplement en utilisant les méthodes de l'API Collection.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
        
public class TestJDOM65 extends TestJDOM {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));
      
      Element livre2 = (Element) document.getRootElement().getChildren().get(1);
      
      List fils = livre2.getChildren();
      
      // Ajouter un nouvel élément

      fils.add(new Element("publication").setText("1996"));
      // Ajouter un nouveau noeud après le second noeud

      fils.add(1, new Element("isbn").setText("0000000000"));
      
      
      Element livre3 = (Element) document.getRootElement().getChildren().get(2);
      fils = livre3.getChildren();
      // supprimer le second noeud

      fils.remove(1);
      // Supprimer tous les neouds nommé editeur

      fils.removeAll(livre3.getChildren("editeur"));
   
      afficher(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <isbn>0000000000</isbn>
    <auteur>auteur2</auteur>
    <editeur>editeur2</editeur>
    <publication>1996</publication>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
  </livre>
</bibliotheque>

L'utilisation de l'API collection implique de tenir compte des contraintes qu'elle impose. Par exemple, il faut être vigilent quant aux modifications de la collection lors de son parcours avec un itérateur.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
        
public class TestJDOM69 extends TestJDOM {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));
      
      Element racine = document.getRootElement();
      Element livre2 = (Element) racine.getChildren().get(1);
      
      List livre2Fils = livre2.getChildren();
      Iterator itr = livre2Fils.iterator();
      while (itr.hasNext()) {
        Element fils = (Element) itr.next();
        if ("auteur".equals(fils.getName())) {
          fils.detach();
        }
      }
      
      afficher(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
Exception in thread "main" java.util.ConcurrentModificationException
   at org.jdom.ContentList$FilterListIterator.checkConcurrentModification(ContentList.java:940)
   at org.jdom.ContentList$FilterListIterator.nextIndex(ContentList.java:829)
   at org.jdom.ContentList$FilterListIterator.hasNext(ContentList.java:785)
   at com.jmdoudoux.test.jdom.TestJDOM69.main(TestJDOM69.java:23)

Une exception de type ConcurrentModificationException est levée car la méthode detach() modifie le contenu de la collection de façon concurrente au parcours fait par l'itérateur. Pour pallier ce problème, il ne faut pas utiliser la méthode detach() de la classe Element mais utiliser la méthode remove() de l'itérateur.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
        
public class TestJDOM69 extends TestJDOM {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));
      
      Element racine = document.getRootElement();
      Element livre2 = (Element) racine.getChildren().get(1);
      
      List livre2Fils = livre2.getChildren();
      Iterator itr = livre2Fils.iterator();
      while (itr.hasNext()) {
        Element fils = (Element) itr.next();
        if ("auteur".equals(fils.getName())) {
          itr.remove();
        }
      }
      
      afficher(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <editeur>editeur2</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <auteur>auteur3</auteur>
    <editeur>editeur3</editeur>
  </livre>
</bibliotheque>

 

45.1.8.1. L'obtention du texte d'un élément

La classe Element propose plusieurs méthodes pour obtenir et modifier le texte d'un élément

Méthode

Rôle

String getText()

Renvoyer le texte de l'élément

String getTextTrim()

Renvoyer le texte de l'élément sans les espaces de début et de fin

String getTextNormalize()

Renvoyer le texte de l'élément sans les espaces de début et de fin, de plus tous les espaces consécutifs sont remplacés par un espace unique

Element setText(String)

Forcer la création d'un noeud unique de type texte


Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Element;

public class TestJDOM51 extends TestJDOM {

  public static void main(String[] args) {

    Element texte = new Element("texte");
    texte.setText("  mon   texte avec     des   espaces   ");
    System.out.println("getText()=*"+texte.getText()+"*");
    System.out.println("getTextTrim()=*"+texte.getTextTrim()+"*");
    System.out.println("getTextNormalize()=*"+texte.getTextNormalize()+"*");
  }
}
Résultat :
getText()=*  mon   texte avec     des   espaces   *
getTextTrim()=*mon   texte avec     des   espaces*
getTextNormalize()=*mon texte avec des espaces*

Dans le cas où un commentaire ou une instruction de traitement est inclus dans le texte, celui-ci est ignoré lors de l'appel à la méthode getText().

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Element;
import java.io.IOException;
import java.io.StringReader;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM63 extends TestJDOM {

  public static void main(String[] args) {

    try {
      StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
      sb.append("<?xml-stylesheet type=\"text/xsl\" href=\"bibliotheque.xsl\"?>");
      sb.append("<bibliotheque>");
      sb.append("  <adresse>mon adresse <!-- commentaire -->complete</adresse>");
      sb.append("</bibliotheque>");

      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new StringReader(sb.toString()));

      Element adresse = document.getRootElement().getChild("adresse");
      System.out.println("getText()=*" + adresse.getText() + "*");
      System.out.println("getTextTrim()=*" + adresse.getTextTrim() + "*");
      System.out.println("getTextNormalize()=*" + adresse.getTextNormalize() + "*");
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
getText()=*mon adresse complete*
getTextTrim()=*mon adresse complete*
getTextNormalize()=*mon adresse complete*

De plus, si le texte contient des éléments fils, le texte de ces derniers n'est pas repris par l'appel à la méthode getText().

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Element;
import java.io.IOException;
import java.io.StringReader;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class TestJDOM64 extends TestJDOM {

  public static void main(String[] args) {

    try {
      StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
      sb.append("<bibliotheque>");
      sb.append("  <adresse>mon adresse <b>complete</b> et integrale</adresse>");
      sb.append("</bibliotheque>");

      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new StringReader(sb.toString()));

      Element adresse = document.getRootElement().getChild("adresse");
      System.out.println("getText()=*" + adresse.getText() + "*");
      System.out.println("getTextTrim()=*" + adresse.getTextTrim() + "*");
      System.out.println("getTextNormalize()=*" + adresse.getTextNormalize() + "*");
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
getText()=*mon adresse  et integrale*
getTextTrim()=*mon adresse  et integrale*
getTextNormalize()=*mon adresse et integrale*

Pour obtenir l'intégralité du texte incluant le texte des éléments fils, il est nécessaire d'écrire un morceau de code qui va parcourir le noeud et ses éléments fils en concaténant le résultat des appels à la méthode getText().

 

45.1.8.2. La modification du texte d'un élément

La méthode setText() permet de modifier le texte d'un élément. Attention, son utilisation supprime tous les noeuds de l'élément déjà existant.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM52 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("racine");
    Document document = new Document(racine);
    Element texte = new Element("test");
    racine.addContent(texte);
    Element fils = new Element("fils");
    texte.addContent(fils);
    Comment commentaire = new Comment("mon commentaire");
    texte.addContent(commentaire);
    texte.setText("mon texte");
    
    afficher(document);
  }
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<racine>
  <test>mon texte</test>
</racine>

Pour éviter la suppression des noeuds fils, il faut utiliser la méthode addContent() plutôt que la méthode setText().

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM53 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("racine");
    Document document = new Document(racine);
    Element texte = new Element("test");
    racine.addContent(texte);
    Element fils = new Element("fils");
    texte.addContent(fils);
    Comment commentaire = new Comment("mon commentaire");
    texte.addContent(commentaire);
    texte.addContent("mon texte");
    
    afficher(document);
  }
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<racine>
  <test>
    <fils />
    <!--mon commentaire-->
    mon texte
  </test>
</racine>

Il ne faut pas utiliser de séquences d'échappement dans le texte d'un élément. JDOM prend le texte tel quel et les caractères seront échappés lors de l'exportation du document.

Exemple :
package com.jmdoudoux.test.jdom;

import org.jdom.Document;
import org.jdom.Element;

public class TestJDOM54 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("racine");
    Document document = new Document(racine);
    Element texte = new Element("texte");
    racine.addContent(texte);
    Element test1 = new Element("test1");
    test1.setText("&#002A;");
    texte.addContent(test1);
    Element test2 = new Element("test2");
    test2.setText("\u002A");
    texte.addContent(test2);
    
    afficher(document);
  }
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<racine>
  <texte>
    <test1>&amp;#002A;</test1>
    <test2>*</test2>
  </texte>
</racine>

 

45.1.8.3. L'obtention du texte d'un élément fils

Il est fréquent dans un document de vouloir obtenir le texte d'un élément fils.

La classe Element propose plusieurs méthodes pour obtenir facilement le texte d'un élément fils. Ces méthodes possèdent deux surcharges : une attendant en paramètre le nom du tag fils, l'autre le nom du tag fils et son espace de nommage.

Méthode

Rôle

String getChildText()

Renvoyer le texte du premier élément fils

String getChildTextTrim()

Renvoyer le texte du premier élément fils sans les espaces de début et de fin

String getChildTextNormalize()

Renvoyer le texte du premier élément fils sans les espaces de début et de fin, de plus tous les espaces consécutifs sont remplacés par un espace unique


Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;

public class TestJDOM55 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      Namespace ns = Namespace.getNamespace("http://www.jmdoudoux.com");
      
      Element elementRacine = document.getRootElement();
      Element elementLivre = elementRacine.getChild("livre", ns);
      String titre = elementLivre.getChildText("titre", ns);
      System.out.println("titre du premier livre = "+titre);
      String auteur = elementLivre.getChildText("auteur", ns);
      System.out.println("auteur du premier livre = "+auteur);
      String editeur = elementLivre.getChildText("editeur", ns);
      System.out.println("editeur du premier livre = "+editeur);
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
titre du premier livre = titre1
auteur du premier livre = auteur1
editeur du premier livre = editeur1

Il est préférable de valider le document XML utilisé avant d'utiliser ces méthodes :

Il ne faut les utiliser que pour des éléments fils uniques qui ne contiennent que des noeuds de type #PCDATA.

 

45.1.8.4. L'ajout et la suppression des fils

La classe Element possède plusieurs surcharges de la méthode addContent() pour ajouter des noeuds fils à l'élément.

Méthode

Rôle

addContent(Content)

Ajouter le noeud fourni à la suite des noeuds existants

addContent(int, Content)

Ajouter le noeud fourni à l'index fourni

addContent(Collection)

Ajouter la collection de noeuds fournie à la suite des noeuds existants

addContent(int, Collection)

Ajouter la collection de noeuds fournie à l'index indiqué

addContent(String)

Ajouter un noeud de type Text


Toutes ces méthodes peuvent lever une exception de type IllegalAddException et renvoient l'élément lui-même.

La classe Element possède plusieurs surcharges des méthodes removeContent(), removeChild() et removeChildren() pour supprimer des noeuds fils de l'élément.

Méthode

Rôle

removeContent()

Supprimer tous les noeuds fils

removeContent(Content)

Supprimer le noeud fils fourni

removeContent(int)

Supprimer le noeud dont l'index est fourni

removeContent(Filter)

Supprimer les noeuds fils correspondant au filtre fourni

removeChild(String)

Supprimer le premier noeud fils dont le nom est fourni

removeChild(String, Namespace)

Supprimer le premier noeud fils dont le nom et l'espace de nommage sont fournis

removeChildren(String)

Supprimer les noeuds fils dont le nom est fourni

removeChildren(String, Namespace)

Supprimer les noeuds fils dont le nom et l'espace de nommage sont fournis


La méthode removeChildren() ne supprime que les éléments fils de premier niveau répondant aux critères de nom et d'espace de nommage fournis.

Si aucun espace de nommage n'est fourni, seuls les éléments sans espace de nommage entrent dans les critères. Les autres noeuds fils tels que du texte, des commentaires ou des instructions de traitement ne sont pas supprimés.

La suppression d'un élément supprime l'élément mais aussi toute l'arborescence fille de l'élément.

Il est aussi possible d'obtenir une collection des noeuds fils en utilisant la méthode getContent() et de manipuler le contenu de cette collection en utilisant les méthodes add() et remove() de la collection.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
        
public class TestJDOM70 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));
      
      Element racine = document.getRootElement();
      supprimerNoeudsTousParNom(racine, "auteur");
      
      afficher(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     

  /**
   * Supprime récursivement tous les éléments ayant pour nom celui fourni en paramètre
   * @param element element à traiter
   * @param nom nom des éléments à supprimer
   */
  public static void supprimerNoeudsTousParNom(Element element, String nom) {
    
    List elements = element.getChildren(nom);
    elements.removeAll(elements);
    
    // recherche de nouveau des fils puisque la collection a potentiellement été modifiée

    List fils = element.getChildren();
    Iterator iterator = fils.iterator();
    while (iterator.hasNext()) {
      supprimerNoeudsTousParNom((Element) iterator.next(), nom);  
    } 
  }
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <editeur>editeur2</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <editeur>editeur3</editeur>
  </livre>
</bibliotheque>

 

45.1.8.5. Le déplacement d'un ou des éléments

Lors de la création d'une instance de type Element, cet élément n'est pas associé à un document. Dans ce cas, la méthode getDocument() renvoie null.

JDOM interdit cependant qu'un Element soit inclus dans deux documents.

Pour déplacer un élément dans un même document ou dans un autre document, il est nécessaire d'invoquer sa méthode detach() au préalable.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;

public class TestJDOM56 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document documentSource = builder.build(new File("bibliotheque.xml"));

      Namespace ns = Namespace.getNamespace("http://www.jmdoudoux.com");
      
      Element elementLivre = documentSource.getRootElement().getChild("livre", ns);

      Element elementRacine = new Element("Bibliotheque");      
      Document documentCible = new Document(elementRacine);

      elementLivre.detach();
      elementRacine.addContent(elementLivre);
      
      afficher(documentSource);
      afficher(documentCible);
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque xmlns="http://www.jmdoudoux.com">
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <auteur>auteur2</auteur>
    <editeur>editeur2</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <auteur>auteur3</auteur>
    <editeur>editeur3</editeur>
  </livre>
</bibliotheque>

<?xml version="1.0" encoding="UTF-8"?>
<Bibliotheque>
  <livre xmlns="http://www.jmdoudoux.com">
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
</Bibliotheque>

Le déplacement d'un élément implique le déplacement de ses noeuds fils. Les espaces de nommage sont aussi gérés lors de ce déplacement.

 

45.1.8.6. La duplication d'un élément

Il arrive parfois qu'il faille dupliquer un élément. Pour cela, il suffit simplement d'utiliser la méthode clone() sur l'instance de l'Element.

L'élément est dupliqué en incluant tous ses noeuds descendants.

Il ne reste plus qu'à ajouter le nouvel élément dans l'arborescence du document.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
        
public class TestJDOM67 extends TestJDOM {
  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));
      
      Element racine = document.getRootElement();
      
      Element livre2 = (Element) racine.getChildren().get(1);
      
      racine.addContent((Element) livre2.clone());
      
      afficher(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <auteur>auteur2</auteur>
    <editeur>editeur2</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <auteur>auteur3</auteur>
    <editeur>editeur3</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <auteur>auteur2</auteur>
    <editeur>editeur2</editeur>
  </livre>
</bibliotheque>

La méthode cloneContent() renvoie une collection de noeuds fils dupliqués.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
        
public class TestJDOM68 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("bibliotheque.xml"));
      
      Element racine = document.getRootElement();
      
      Element livre2 = (Element) racine.getChildren().get(1);
      
      racine.addContent(livre2.cloneContent());
      
      afficher(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <!-- commentaires livre 1 -->
    <titre>titre1</titre>
    <auteur>auteur1</auteur>
    <editeur>editeur1</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 2 -->
    <titre>titre2</titre>
    <auteur>auteur2</auteur>
    <editeur>editeur2</editeur>
  </livre>
  <livre>
    <!-- commentaires livre 3 -->
    <titre>titre3</titre>
    <auteur>auteur3</auteur>
    <editeur>editeur3</editeur>
  </livre>
  <!-- commentaires livre 2 -->
  <titre>titre2</titre>
  <auteur>auteur2</auteur>
  <editeur>editeur2</editeur>
</bibliotheque>

 

45.1.9. L'utilisation de filtres

JDOM propose l'interface org.jdom.filter.Filter qui permet de définir un filtre.

L'interface Filter ne définit que la méthode matches() qui attend en paramètre un objet correspondant à un noeud et renvoie un booléen pour préciser si l'objet répond ou non aux critères du filtre.

Ce filtre peut être utilisé par plusieurs méthodes de certaines classes de JDOM pour restreindre leur action sur les entités qui répondent aux critères du filtre.

JDOM propose deux implémentations de l'interface Filter :

La classe ContentFilter possède plusieurs constructeurs :

Constructeur

Rôle

ContentFilter()

Filtre acceptant tous les types de noeuds

ContentFilter(int)

Filtre acceptant uniquement les noeuds précisés dans le masque (le masque utilise les constantes définies dans la classe ContentFilter)

ContentFilter(boolean)

Filtre acceptant ou non tous les types de noeuds selon la valeur du paramètre


La classe ContentFilter propose plusieurs méthodes setXXXVisible() qui attendent un paramètre de type booléen permettant d'inclure ou non les noeuds de type XXX.

Exemple : afficher tous les commentaires du document
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.filter.ContentFilter;
import org.jdom.input.SAXBuilder;

public class TestJDOM57 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document documentSource = builder.build(new File("bibliotheque.xml"));

      Element elementRacine = documentSource.getRootElement();    
      process(elementRacine);

    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
  
  public static void process(Element element) {
    List children = element.getContent();
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      Object o = iterator.next();
      if (o instanceof Element) {
        Element child = (Element) o;
        afficherCommentaires(child);
        process(child);
      }
    }
  }

  public static void afficherCommentaires(Element element) {
    ContentFilter filtre = new ContentFilter(false);
    filtre.setCommentVisible(true);

    List children = element.getContent(filtre);
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      Comment comment = (Comment) iterator.next();
      System.out.println(comment.getText());
    }
  }
}
Résultat :
commentaires livre 1 
commentaires livre 2
commentaires livre 3

Le classe ElementFilter possède plusieurs constructeurs :

Constructeur

Rôle

ElementFilter()

Filtre qui ne renvoie que les noeuds de type Element

ElementFilter(String)

Filtre qui ne renvoie que les éléments dont le nom est fourni

ElementFilter(Namespace)

Filtre qui ne renvoie que les éléments dont l'espace de nommage est fourni

ElementFilter(String, Namespace)

Filtre qui ne renvoie que les éléments dont le nom et l'espace de nommage sont fournis


Exemple : afficher le titre de tous les livres
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.filter.ElementFilter;
import org.jdom.input.SAXBuilder;

public class TestJDOM58 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document documentSource = builder.build(new File("bibliotheque.xml"));

      Element elementRacine = documentSource.getRootElement();    
      Iterator iterator = elementRacine.getContent().iterator();
      while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof Element) {
          Element child = (Element) o;
          afficherTitre(child);
        }
      }
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
  
  public static void afficherTitre(Element element) {
    ElementFilter filtre = new ElementFilter("titre");

    List children = element.getContent(filtre);
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      Element fils = (Element) iterator.next();
      System.out.println(fils.getText());
    }
  }
}
Résultat :
titre1
titre2
titre3

Il est aussi possible de définir son propre filtre en créant une classe qui implémente l'interface Filter. Il suffit de définir la méthode matches() en lui faisant renvoyer un booléen indiquant le résultat de l'application des critères de filtre sur l'objet fourni en paramètres.

Exemple : afficher les auteurs de chaque livre

Exemple : afficher les auteurs de chaque livre
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.filter.Filter;
import org.jdom.input.SAXBuilder;

public class TestJDOM59 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document documentSource = builder.build(new File("bibliotheque.xml"));

      Element elementRacine = documentSource.getRootElement();    
      Iterator iterator = elementRacine.getContent().iterator();
      while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof Element) {
          Element child = (Element) o;
          afficherTitre(child);
        }
      }
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }

  public static void afficherTitre(Element element) {
    Filter filtre = new Filter() {
      private static final long serialVersionUID = 1L;

      public boolean matches(Object arg0) {
        boolean resultat = false;

        if(arg0 instanceof Element){
          Element element = (Element)arg0;   
          resultat = "auteur".equals(element.getName());
        }
        return resultat;
      }
    };

    List children = element.getContent(filtre);
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      Element fils = (Element) iterator.next();
      System.out.println(fils.getText());
    }
  }
}
Résultat :
auteur1
auteur2
auteur3

Cet exemple n'a qu'un intérêt pédagogique puisque la même opération peut être réalisée en utilisant la méthode getChildText(). En pratique les filtres personnalisés sont plus complexes.

 

45.1.10. L'exportation d'un document

JDOM prévoit plusieurs classes pour permettre d'exporter le document contenu dans un objet de type Document. Cette exportation peut se faire :

Les classes nécessaires à ces traitements sont regroupées dans le package org.jdom.output.

 

45.1.10.1. L'exportation dans un flux

La classe XMLOutputter permet d'envoyer le document XML dans un flux. Il est possible de fournir plusieurs paramètres pour formater la sortie du document.

Cette classe possède plusieurs constructeurs dont les principaux sont :

Constructeur

Rôle

XMLOutputter()

Créer un objet par défaut, sans paramètre de formatage

XMLOutputter(Format)

Créer un objet, en précisant en paramètre les options de formattage


La mise en oeuvre de cette classe est très simple : il suffit d'instancier un objet de type XMLOutputter et d'invoquer sa méthode output().

Exemple : lecture et exportation sur la console
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
        
public class TestJDOM34 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("test.xml"));
      XMLOutputter sortie = new XMLOutputter();
      sortie.output(document, System.out);

    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}

La classe XMLOuputter possède de nombreuses surcharges de la méthode output() permettant l'exportation dans un flux OutputStream ou Writer de différentes entités (Document, Element, Comment, Text, EntityRef, ProcessingInstruction, DocType, ...)

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.IOException;

import org.jdom.Element;
import org.jdom.output.XMLOutputter;
        
public class TestJDOM40 {

  public static void main(String[] args) {
    try {
      Element racine = new Element("html");
      Element body = new Element("body");
      racine.addContent(body);
      Element titre = new Element("H1");
      titre.setText("titre");
      body.addContent(titre);
      
      XMLOutputter sortie = new XMLOutputter();
      sortie.output(racine, System.out);
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat :
<html><body><H1>titre</H1></body></html>

Il est possible de configurer les options de formattage de l'exportation en utilisant un objet de type org.jdom.output.Format.

La classe Format propose trois formats prédéfinis que l'on peut obtenir en invoquant la méthode statique correspondante :

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
        
public class TestJDOM35 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("test.xml"));
      XMLOutputter sortie = new XMLOutputter(Format.getCompactFormat());
      sortie.output(document, System.out);

    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}
Résultat avec CompactFormat
<?xml version="1.0" encoding="UTF-8"?>
<html><head /><body><table width="100%" border="0" /></body></html>
Résultat avec PrettyFormat
<?xml
version="1.0" encoding="UTF-8"?>
<html>
  <head />
  <body>
    <table width="100%" border="0" />
  </body>
</html>
Résultat avec RawFormat
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head />
<body>
<table width="100%" border="0" />
</body>
</html>

Pour des besoins plus spécifiques, il est possible d'instancier un objet Format et de le configurer en utilisant ses différentes méthodes.

Par défaut, XMLOutputter utilise l'encodage des caractères en UTF-8. Pour préciser un autre encodage des caractères, il faut utiliser la méthode setEncoding() de l'objet Format utilisé par XMLOutputter.

Le flux utilisé peut être de type OutputStream ou Writer. L'utilisation d'un OutputStream est plus facile car le Writer impose de préciser l'encodage de caractères correspondant à celui déclaré dans le prologue du document.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public class TestJDOM38 extends TestJDOM {

  public static void main(String[] args) {

    Element racine = new Element("bibliotheque");
    Document document = new Document(racine);
    Element livres = new Element("livres");
    racine.addContent(livres);
    
    Element livre = new Element("livre");
    livres.addContent(livre);
    
    Element titre = new Element("titre").setText("Titre livre 1");
    Element auteur = new Element("auteur").setText("Auteur 1");
    livre.addContent(titre);
    livre.addContent(auteur);

    Format format = Format.getPrettyFormat();
    format.setEncoding("ISO-8859-1");
    XMLOutputter sortie = new XMLOutputter(format);

    FileOutputStream fos = null;
    try {
      fos = new FileOutputStream("c:/temp/test.xml"); 
      OutputStreamWriter out = new OutputStreamWriter(fos, "ISO-8859-1"); 
      sortie.output(document, out);
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
}

La classe XMLOutputter propose aussi la méthode ouputString() qui exporte différentes entités selon la surcharge utilisée dans une chaîne de caractères.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
        
public class TestJDOM36 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("test.xml"));
      XMLOutputter sortie = new XMLOutputter(Format.getCompactFormat());
      String docXML = sortie.outputString(document);
      System.out.println(docXML);

    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}

La classe XMLOutputter se charge de convertir les caractères utilisés par XML en leurs entités respectives durant l'exportation.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.IOException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public class TestJDOM37  {

  public static void main(String[] args) {
    Element racine = new Element("personne");
    Document document = new Document(racine);
    Element adresse = new Element("adresse");
    adresse.addContent("mon adresse < 5 et > 10 & impaire ");
    racine.addContent(adresse);
    XMLOutputter sortie = new XMLOutputter(Format.getRawFormat());
    try {
      sortie.output(document, System.out);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<personne><adresse>mon adresse &lt; 5 et &gt; 10 &amp; impaire </adresse></personne>

 

45.1.10.2. L'exportation dans un arbre DOM

La classe org.jdom.ouput.DOMOutputter permet d'exporter un document JDOM dans un arbre DOM.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.DOMOutputter;
        
public class TestJDOM39 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      Document document = builder.build(new File("test.xml"));
      
      DOMOutputter domOutputter = new DOMOutputter();
      org.w3c.dom.Document documentDOM = domOutputter.output(document);
    } catch(JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }   
  }     
}

Cette exportation est plutôt une conversion de l'arbre d'objets du modèle JDOM vers le modèle DOM.

Les deux arbres d'objets sont indépendants l'un de l'autre : une modification dans l'arbre JDOM après l'exportation doit être faite aussi dans l'arbre DOM ou il est nécessaire de refaire une exportation pour refléter la modification dans l'arbre DOM

 

45.1.10.3. L'exportation en SAX

La classe SAXOutputter permet de générer des événements SAX à partir d'un document JDOM.

La classe SAXOutputter possède plusieurs constructeurs qui attendent tous un objet de type ContentHandler et certains des objets de type ErrorHandler, DTDHandler, EntityResolver et LexicalHandler.

La méthode output() parcourt l'arbre JDOM et émet les événements SAX correspondants qui seront traités par les handlers.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.SAXOutputter;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class TestJDOM50 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      MonSAXHandler handler = new MonSAXHandler();
      SAXOutputter outputter = new SAXOutputter(handler);
      outputter.setErrorHandler(handler);
      outputter.setDTDHandler(handler);
      outputter.output(document);

    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}

class MonSAXHandler extends DefaultHandler {
  private String tagCourant = "";

  /**
   * Actions à réaliser lors de la détection d'un nouvel élément.
   */
  public void startElement(String nameSpace,String localName,String qName, 
    Attributes attr) throws SAXException {
    tagCourant = localName;
    System.out.println("debut tag : " + localName);
  }

  /**
   * Actions à réaliser lors de la détection de la fin d'un élément.
   */
  public void endElement(String nameSpace, String localName, String qName) 
    throws SAXException {
    tagCourant = "";
    System.out.println("Fin tag " + localName);
  }

  /**
   * Actions à réaliser au début du document.
   */
  public void startDocument() {
    System.out.println("Debut du document");
  }

  /**
   * Actions à réaliser lors de la fin du document XML.
   */
  public void endDocument() {
    System.out.println("Fin du document");
  }

  /**
   * Actions à réaliser sur les données
   */
  public void characters(char[] caracteres, int debut, int longueur) throws SAXException {
    String donnees = new String(caracteres, debut, longueur);
    if (!tagCourant.equals("")) {
      if (!Character.isISOControl(caracteres[debut])) {
        System.out.println(" Element " + tagCourant + ", valeur = *" + donnees + "*");
      }
    }
  }
}
Résultat :
Debut du document
debut tag : bibliotheque
debut tag : livre
debut tag : titre
 Element titre, valeur = *titre1*
Fin tag titre
debut tag : auteur
 Element auteur, valeur = *auteur1*
Fin tag auteur
debut tag : editeur
 Element editeur, valeur = *editeur1*
Fin tag editeur
Fin tag livre
debut tag : livre
debut tag : titre
 Element titre, valeur = *titre2*
Fin tag titre
debut tag : auteur
 Element auteur, valeur = *auteur2*
Fin tag auteur
debut tag : editeur
 Element editeur, valeur = *editeur2*
Fin tag editeur
Fin tag livre
debut tag : livre
debut tag : titre
 Element titre, valeur = *titre3*
Fin tag titre
debut tag : auteur
 Element auteur, valeur = *auteur3*
Fin tag auteur
debut tag : editeur
 Element editeur, valeur = *editeur3*
Fin tag editeur
Fin tag livre
Fin tag bibliotheque
Fin du document

 

45.1.11. L'utilisation de XSLT

La classe org.jdom.transform.XSLTransformer est un helper qui facilite la mise en oeuvre de transformations simples. Cette classe utilise l'API TrAX de JAXP. Le moteur de transformation utilisé doit être paramétré en utilisant JAXP.

La classe XSLTransformer possède plusieurs constructeurs qui attendent en paramètre une feuille de style XSL.

Elle possède plusieurs surcharges de la méthode transform() qui appliquent la feuille de style à un document ou un ensemble de noeuds.

Exemple :
package com.jmdoudoux.test.jdom;
	  
import java.io.File;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.transform.XSLTransformer;

public class TestJDOM62 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document documentSource = builder.build(new File("bibliotheque.xml"));

      XSLTransformer transformer = new XSLTransformer("bibliotheque.xsl");
      Document documentCible = transformer.transform(documentSource);

      afficher(documentCible);
      
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Exemple : la feuille de style bibliotheque.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
    <body>
      <h2>bibliotheque</h2>
      <table border="1">
        <tr bgcolor="lightblue">
          <th align="left">Titre</th>
          <th align="left">Auteur</th>
        </tr>
        <xsl:for-each select="bibliotheque/livre">
          <tr>
            <td>
              <xsl:value-of select="titre" />
            </td>
            <td>
              <xsl:value-of select="auteur" />
            </td>
          </tr>
        </xsl:for-each>
      </table>
    </body>
  </html>
</xsl:template>
</xsl:stylesheet>
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<html>
  <body>
    <h2>bibliotheque</h2>
    <table border="1">
      <tr bgcolor="lightblue">
        <th align="left">Titre</th>
        <th align="left">Auteur</th>
      </tr>
      <tr>
        <td>titre1</td>
        <td>auteur1</td>
      </tr>
      <tr>
        <td>titre2</td>
        <td>auteur2</td>
      </tr>
      <tr>
        <td>titre3</td>
        <td>auteur3</td>
      </tr>
    </table>
  </body>
</html>

Les méthodes transform() qui attendent en paramètre un objet de type document retournent un objet JDOM de type Document encapsulant le résultat de la transformation. Ceci est particulièrement adapté lorsqu'un document XML est transformé en un autre document XML.

Pour des besoins plus spécifiques, il est possible d'obtenir une instance de la classe Transformer de JAXP et d'utiliser les classes org.jdom.transform.JDOMSource et JDOMResult comme wrapper respectivement en entrée et en sortie de la transformation.

Ceci est nécessaire par exemple lorsque des paramètres doivent être passés à la feuille de style.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.transform.JDOMResult;
import org.jdom.transform.JDOMSource;

public class TestJDOM75 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document documentSource = builder.build(new File("bibliotheque.xml"));

      TransformerFactory factory = TransformerFactory.newInstance();
      Transformer transformer = factory.newTransformer(new StreamSource("biblio.xsl"));

      JDOMSource source = new JDOMSource(documentSource);
      JDOMResult resultat = new JDOMResult();
      
      transformer.setParameter("libelle", "mon libelle");
      transformer.transform(source, resultat);

      afficher(resultat.getDocument());
    } catch (JDOMException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (TransformerException e) {
      e.printStackTrace();
    }
  }
}
Exemple : la feuille de style qui déclare et utilise un paramètre nommé libelle
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="libelle" select=""/>
<xsl:template match="/">
  <html>
    <body>
      <h2>bibliotheque : <xsl:value-of select="$libelle"/> </h2>
      <table border="1">
        <tr bgcolor="lightblue">
          <th align="left">Titre</th>
          <th align="left">Auteur</th>
        </tr>
        <xsl:for-each select="bibliotheque/livre">
          <tr>
            <td>
              <xsl:value-of select="titre" />
            </td>
            <td>
              <xsl:value-of select="auteur" />
            </td>
          </tr>
        </xsl:for-each>
      </table>
    </body>
  </html>
</xsl:template>
</xsl:stylesheet>
Résultat :
<?xml version="1.0" encoding="UTF-8"?>
<html>
  <body>
    <h2>bibliotheque : mon libelle</h2>
    <table border="1">
      <tr bgcolor="lightblue">
        <th align="left">Titre</th>
        <th align="left">Auteur</th>
      </tr>
      <tr>
        <td>titre1</td>
        <td>auteur1</td>
      </tr>
      <tr>
        <td>titre2</td>
        <td>auteur2</td>
      </tr>
      <tr>
        <td>titre3</td>
        <td>auteur3</td>
      </tr>
    </table>
  </body>
</html>

 

45.1.12. L'utilisation de XPath

JDOM propose un support de XPath en utilisant Jaxen depuis sa version bêta 9.

Les bibliothèques de Jaxen doivent être ajoutées au classpath : elles sont fournies dans le sous-répertoire lib de l'archive binaire de JDOM.

Elles peuvent aussi être téléchargées sur le site http://jaxen.org/

Il faut ajouter au classpath les bibliothèques : jaxen-core.jar, jaxen-jdom.jar et saxpath.jar.

Il faut instancier un objet de type org.jdom.xpath.XPath en utilisant sa méthode statique newInstance() qui attend en paramètre l'expression XPath.

L'invocation de la méthode selectNodes() sur cette instance en lui passant en paramètre le document renvoie une collection des noeuds qui répond à l'expression XPath.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

public class TestJDOM60 extends TestJDOM {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      XPath x      = XPath.newInstance("/bibliotheque/livre");
      List list    = x.selectNodes(document);
      
      System.out.println("nb livres="+list.size());
      
      Iterator iterator = list.iterator();
      while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof Element) {
          Element livre = (Element) o;
          System.out.println("livre : "+livre.getChildText("titre"));
        }
      }
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
nb livres=3
livre : titre1
livre : titre2
livre : titre3

Le type des objets contenus dans la collection retournée par la méthode dépend de l'expression XPath fournie.

Exemple :
package com.jmdoudoux.test.jdom;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.Text;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

public class TestJDOM61 {

  public static void main(String[] args) {
    try {
      SAXBuilder builder = new SAXBuilder();
      builder.setIgnoringElementContentWhitespace(true);
      Document document = builder.build(new File("bibliotheque.xml"));

      XPath x      = XPath.newInstance("/bibliotheque/livre/titre/text()");
      List list    = x.selectNodes(document);
      
      System.out.println("nb titres="+list.size());
      
      Iterator iterator = list.iterator();
      while (iterator.hasNext()) {
        Text texte  = (Text) iterator.next();
        System.out.println("livre : "+texte.getValue());
      }
    } catch (JDOMException e) {
      e.printStackTrace(System.out);
    } catch (IOException e) {
      e.printStackTrace(System.out);
    }
  }
}
Résultat :
nb titres=3
livre : titre1
livre : titre2
livre : titre3

La méthode selectSingleNode() peut être utilisée à la place de la méthode selectNodes() lorsque l'expression ne renvoie qu'un seul noeud.

 

45.1.13. L'intégration à Java

JDOM est une API développée en Java pour Java : elle repose sur des API de Java telles que l'API Collection et met en oeuvre certaines fonctionnalités de Java notamment le clonage ou la sérialisation.

JDOM a délibérément été voulue non thread safe puisque cela devrait être la plus large utilisation de l'API. Pour une utilisation des objets dans un contexte multithread, il faut procéder manuellement à une synchronisation des portions de code critiques.

Les méthodes equals() sont redéfinies pour ne retourner l'égalité que si les deux objets sont exactement les mêmes (test sur les références effectué par l'opérateur ==).

Ce test d'égalité permet de garantir que l'élément cible est bien celui concerné notamment en tenant compte de sa position dans le document. Il peut, par exemple, y avoir plusieurs éléments avec le même nom et la même valeur dans un même document. Le fait d'avoir des instances distinctes garantit de manipuler l'élément concerné.

De plus, les méthodes equals() et hashCode() sont déclarées final pour ne pas permettre de déroger à cette règle de comparaison.

Les classes qui encapsulent des données du document implémentent l'interface Serializable (sauf la classe Namespace) ainsi ces objets peuvent être sérialisés pour les rendre persistants ou permettre leur échange à travers le réseau.

Les classes qui encapsulent des données du document redéfinissent la méthode toString() pour retourner une représentation textuelle des données qu'elles encapsulent entre crochets en précisant le type de l'entité.

Attention : la méthode toString() ne renvoie pas de représentation au format XML de l'entité. Pour obtenir cette représentation, il faut utiliser un objet de type XMLOutputter.

Les classes qui encapsulent des données du document implémentent l'interface Cloneable sauf la classe Namespace qui encapsule un objet immuable. Le clonage d'un élément se fait de façon récursive : seul le parent de l'objet original n'est pas repris dans la copie.

L'utilisation de l'API Collection pour encapsuler un ensemble d'entités implique que toutes les modifications sur une collection affecte le document.

De nombreuses méthodes de l'API JDOM peuvent lever une exception qui hérite de la classe JDOMException. Ceci permet de faire un traitement générique sur ces exceptions ou de faire un traitement sur une exception en particulier.

 

45.1.14. Les contraintes de la mise en oeuvre de JDOM

La mise en oeuvre de JDOM présente plusieurs contraintes dont il faut tenir compte.

L'utilisation de JDOM implique la création en mémoire de l'arbre d'objets encapsulant le document ce qui peut être difficile dans un environnement ayant peu de ressources, notamment mémoire, ou pour traiter de gros documents.

JDOM ne propose pas de support complet pour les DTD ou les schémas : il n'est pas possible de s'assurer que le document est valide lors d'une modification.

 

45.2. dom4j

dom4j est un framework open source pour manipuler des données XML, XSL et Xpath. Il est entièrement développé en Java et pour Java.

Dom4j n'est pas un parser mais propose un modèle de représentation d'un document XML et une API pour en faciliter l'utilisation. Pour obtenir une telle représentation, dom4j utilise soit SAX, soit DOM. Comme il est compatible JAXP, il est possible d'utiliser tout parser qui implémente cette API.

La version de dom4j utilisée dans cette section est la 1.3

 

45.2.1. L'installation de dom4j

Il faut télécharger la dernière version à l'url http://www.dom4j.org/download.html

Il suffit de décompresser le fichier téléchargé. Celui-ci contient de nombreuses bibliothèques (ant, xalan, xerces, crimson, junit, ...), le code source du projet et la documentation.

Le plus simple pour utiliser rapidement dom4j est de copier les fichiers jar contenus dans le répertoire lib dans le répertoire ext du répertoire %JAVA_HOME%/jre/lib/ext ainsi que les fichiers dom4j.jar et dom4j-full.jar.

 

45.2.2. La création d'un document

Dom4j encapsule un document dans un objet de type org.dom4j.Document. Dom4j propose des API pour facilement créer un tel objet qui va être le point d'entrée de la représentation d'un document XML .

Exemple utilisant SAX :
import org.dom4j.*;
import org.dom4j.io.*;

public class Testdom4j_1 {

  public static void main(String args[]) {
    DOCUMENT DOCUMENT;
    try {
      SAXReader xmlReader = new SAXReader();
      document = xmlReader.read("test.xml");
    } catch (Exception e){
      e.printStackTrace();
    }
  } 
}

Pour exécuter ce code, il suffit d'exécuter

java -cp .;%JAVA_HOME%/jre/lib/ext/dom4j-full.jar Testdom4j_1

Exemple à partir d'une chaîne de caractères :
import org.dom4j.*;

public class Testdom4j_8 {

  public static void main(String args[]) {
    Document document = null;
    String texte = "<bibliotheque><livre><titre>titre 1</titre><auteur>auteur 1</auteur>"
         +"<editeur>editeur 1</editeur></livre></bibliotheque>";
    try {
      document = DocumentHelper.parseText(texte);
    } catch (Exception e){
     e.printStackTrace();
    }
  }
}

45.2.3. Le parcours d'un document

Le parcours du document construit peut se faire de plusieurs façons :

Le parcours peut se faire en utilisant l'API collection de Java.

Exemple : obtenir tous les noeuds fils du noeud racine
import org.dom4j.*;
import org.dom4j.io.*;
import java.util.*;

public class Testdom4j_2 {

  public static void main(String args[]) {
    Document document;
    org.dom4j.Element racine; 
    try {
      SAXReader xmlReader = new SAXReader();
      document = xmlReader.read("test.xml");
      racine = document.getRootElement();
      Iterator it = racine.elementIterator();
      while(it.hasNext()){ 
        Element element = (Element)it.next(); 
        System.out.println(element.getName()); 
      } 
    } catch (Exception e){
      e.printStackTrace();
    }
  }
}

Un des grands intérêts de dom4j est de proposer une recherche dans le document en utilisant la technologie Xpath.

Exemple : obtenir tous les noeuds fils du noeud racine
import org.dom4j.*;
import org.dom4j.io.*;
import java.util.*;

public class Testdom4j_3 {

  public static void main(String args[]) {
    Document document;
    try {
      SAXReader xmlReader = new SAXReader();
      document = xmlReader.read("test.xml");
      XPath xpathSelector = DocumentHelper.createXPath("/bibliotheque/livre/auteur"); 
      List liste = xpathSelector.selectNodes(document); 
      for ( Iterator it = liste.iterator(); it.hasNext(); ) { 
        Element element = (Element) it.next(); 
        System.out.println(element.getName()+" : "+element.getText()); 
      }
    } catch (Exception e){
      e.printStackTrace();
    }
  }
}

 

45.2.4. La modification d'un document XML

L'interface Document propose plusieurs méthodes pour modifier la structure du document.

Méthode

Rôle

Document addComment(String)

Ajouter un commentaire

void setDocType(DocumentType)

Modifier les caractéristiques du type de document

void setRootElement(Element)

Modifier l'élément racine du document


L'interface Element propose plusieurs méthodes pour modifier un élément du document.

Méthode

Rôle

void add(...)

Méthode surchargée qui permet d'ajouter un attribut, une entité, un espace nommage ou du texte à l'élément

Element addAttribute(String, String)

Ajouter un attribut à l'élément

Element addComment(String)

Ajouter un commentaire à l'élément

Element addEntity(String, String)

Ajouter une entité à l'élément

Element addNameSpace(String, String)

Ajouter un espace de nommage à l'élément

Element addText(String)

Ajouter un text à l'élément

 

45.2.5. La création d'un nouveau document XML

Il est très facile de créer un document XML

La classe DocumentHelper propose une méthode createDocument() qui renvoie une nouvelle instance de la classe Document. Il suffit alors d'ajouter chacun des noeuds de l'arbre du nouveau document XML en utilisant la méthode addElement() de la classe Document.

Exemple :
import org.dom4j.*;
      
public class Testdom4j_4 {

  public static void main(String args[]) {
    Document document = DocumentHelper.createDocument();
    Element root = document.addElement( "bibliotheque" );
    Element livre = null;
    try {
    livre = root.addElement("livre");
    livre.addElement("titre").addText("titre 1");
    livre.addElement("auteur").addText("auteur 1");
    livre.addElement("editeur").addText("editeur 1");
    } catch (Exception e){
      e.printStackTrace();
    }
  }
}

 

45.2.6. L'exportation d'un document

Pour écrire le document XML dans un fichier, une méthode de la classe Document permet de réaliser cette action très simplement

Exemple :
import org.dom4j.*;
import java.io.*;

public class Testdom4j_5 {

  public static void main(String args[]) {
    Document document = DocumentHelper.createDocument();
    Element root = document.addElement( "bibliotheque" );
    Element livre = null;
    try {
      livre = root.addElement("livre");
      livre.addElement("titre").addText("titre 1");
      livre.addElement("auteur").addText("auteur 1");
      livre.addElement("editeur").addText("editeur 1");
      livre = root.addElement("livre");
      livre.addElement("titre").addText("titre 2");
      livre.addElement("auteur").addText("auteur 2");
      livre.addElement("editeur").addText("editeur 2");
      livre = root.addElement("livre");
      livre.addElement("titre").addText("titre 3");
      livre.addElement("auteur").addText("auteur 3");
      livre.addElement("editeur").addText("editeur 3");
      FileWriter out = new FileWriter( "test2.xml" ); 
      document.write( out ); 
      out.close();
    } catch (Exception e){
      e.printStackTrace();
    }
  }
}

Pour pouvoir agir sur le formatage du document ou pour utiliser un flux différent, il faut utiliser la classe XMLWriter

Exemple :
import org.dom4j.*;
import org.dom4j.io.*;
import java.io.*;

public class Testdom4j_6 {

  public static void main(String args[]) {
    Document document = DocumentHelper.createDocument();
    Element root = document.addElement( "bibliotheque" );
    Element livre = null;
    try {
      livre = root.addElement("livre");
      livre.addElement("titre").addText("titre 1");
      livre.addElement("auteur").addText("auteur 1");
      livre.addElement("editeur").addText("editeur 1");
      livre = root.addElement("livre");
      livre.addElement("titre").addText("titre 2");
      livre.addElement("auteur").addText("auteur 2");
      livre.addElement("editeur").addText("editeur 2");
      livre = root.addElement("livre");
      livre.addElement("titre").addText("titre 3");
      livre.addElement("auteur").addText("auteur 3");
      livre.addElement("editeur").addText("editeur 3");
      OutputFormat format = OutputFormat.createPrettyPrint(); 
      XMLWriter writer = new XMLWriter( System.out, format ); 
      writer.write( document ); 
    } catch (Exception e){
      e.printStackTrace();
    }
  }
}
Résultat :
C:\test_dom4j>java -cp .;c:\j2sdk1.4.0_01/jre/lib/ext/dom4j
-full.jar Testdom4j_6
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque>
  <livre>
    <titre>titre 1</titre>
    <auteur>auteur 1</auteur>
    <editeur>editeur 1</editeur>
  </livre>
  <livre>
    <titre>titre 2</titre>
    <auteur>auteur 2</auteur>
    <editeur>editeur 2</editeur>
  </livre>
  <livre>
    <titre>titre 3</titre>
    <auteur>auteur 3</auteur>
    <editeur>editeur 3</editeur>
  </livre>
</bibliotheque>

La classe OutputFormat possède une méthode createPrettyPrint() qui renvoie un objet de type OutputFormat contenant des paramètres par défaut.

Il est possible d'obtenir une chaîne de caractères à partir de tout ou partie d'un document.

Exemple :
import org.dom4j.*;
import org.dom4j.io.*;

public class Testdom4j_7 {

  public static void main(String args[]) {
    Document document = DocumentHelper.createDocument();
    Element root = document.addElement( "bibliotheque" );
    Element livre = null;
    String texte = "";
    try {
      livre = root.addElement("livre");
      livre.addElement("titre").addText("titre 1");
      livre.addElement("auteur").addText("auteur 1");
      livre.addElement("editeur").addText("editeur 1");
      texte = document.asXML();
      System.out.println(texte);
    } catch (Exception e){
      e.printStackTrace();
    }
  }
}
Résultat :
C:\test_dom4j>java -cp .;c:\j2sdk1.4.0_01/jre/lib/ext/dom4j
-full.jar Testdom4j_7
<?xml version="1.0" encoding="UTF-8"?>
<bibliotheque><livre><titre>titre 1</titre><auteur>auteur 1</auteur><editeur>edi
teur 1</editeur></livre></bibliotheque>

 

 


[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

72 commentaires Donner une note à l'article (5)

 

Copyright (C) 1999-2016 Jean-Michel DOUDOUX. Vous pouvez copier, redistribuer et/ou modifier ce document selon les termes de la Licence de Documentation Libre GNU, Version 1.1 ou toute autre version ultérieure publiée par la Free Software Foundation; les Sections Invariantes étant constitués du chapitre Préambule, aucun Texte de Première de Couverture, et aucun Texte de Quatrième de Couverture. Une copie de la licence est incluse dans la section GNU FreeDocumentation Licence. La version la plus récente de cette licence est disponible à l'adresse : GNU Free Documentation Licence.

Responsables bénévoles de la rubrique Java : Mickael Baron - Robin56 -