1. DOM (Document Object Model)
Parfois, un fichier XML est trop volumineux pour être chargé entièrement en mémoire. Il arrive aussi que la structure du document soit inconnue à l’avance ou trop complexe. Dans d’autres cas, vous devez traiter uniquement certaines parties des données, et non tout le fichier, ou même modifier le document pendant l’exécution du programme : supprimer un nœud, ajouter un nouvel élément ou réorganiser une partie de la structure.
Dans de telles situations, des outils éprouvés sont adaptés — DOM et SAX. Ils permettent de travailler directement avec le contenu XML sans être liés à des classes Java prédéfinies.
Comment fonctionne DOM
DOM est une façon de représenter un document XML comme un arbre d’objets en mémoire. Chaque balise devient un nœud de l’arbre (Node), et les attributs, les valeurs textuelles et même les commentaires deviennent des objets distincts. Après le chargement du document, vous avez un accès complet à sa structure : vous pouvez lire, modifier, supprimer, ajouter des éléments et des attributs.
Principales classes DOM en Java
- DocumentBuilderFactory — fabrique pour créer un analyseur.
- DocumentBuilder — analyseur qui transforme le XML en arbre.
- Document — objet racine de l’arbre.
- Element — élément XML (balise).
- NodeList — liste de nœuds (par exemple, tous les <item> à l’intérieur de <items>).
Exemple : lecture d’un fichier XML avec DOM
Supposons que nous devions lire un fichier XML de ce type :
<contacts>
<person id="1">
<name>Ivan</name>
<email>ivan@example.com</email>
</person>
<person id="2">
<name>Mariya</name>
<email>maria@example.com</email>
</person>
</contacts>
Code : lecture et parcours des éléments
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.File;
public class DomExample {
public static void main(String[] args) throws Exception {
// 1. Créer la fabrique et l’analyseur
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 2. Charger le fichier XML en mémoire
Document doc = builder.parse(new File("contacts.xml"));
// 3. Obtenir l’élément racine
Element root = doc.getDocumentElement();
System.out.println("Élément racine: " + root.getTagName());
// 4. Obtenir la liste de tous les <person>
NodeList persons = root.getElementsByTagName("person");
for (int i = 0; i < persons.getLength(); i++) {
Element person = (Element) persons.item(i);
String id = person.getAttribute("id");
String name = person.getElementsByTagName("name").item(0).getTextContent();
String email = person.getElementsByTagName("email").item(0).getTextContent();
System.out.println("id: " + id + ", name: " + name + ", email: " + email);
}
}
}
Ce qui se passe ici :
- Nous créons d’abord l’analyseur et chargeons le fichier XML.
- Nous récupérons l’élément racine (contacts).
- Nous trouvons tous les éléments <person> et les parcourons.
- Pour chaque <person>, nous lisons l’attribut id ainsi que les éléments <name> et <email>.
Schéma de l’arbre DOM pour l’exemple
contacts
├── person (id="1")
│ ├── name ("Ivan")
│ └── email ("ivan@example.com")
└── person (id="2")
├── name ("Mariya")
└── email ("maria@example.com")
Modifier du XML avec DOM
DOM permet non seulement de lire, mais aussi de modifier du XML. Par exemple, ajoutons une nouvelle personne :
// Créer un nouvel élément <person>
Element newPerson = doc.createElement("person");
newPerson.setAttribute("id", "3");
// <name>
Element name = doc.createElement("name");
name.setTextContent("Sergey");
newPerson.appendChild(name);
// <email>
Element email = doc.createElement("email");
email.setTextContent("sergey@example.com");
newPerson.appendChild(email);
// Ajouter à la racine
root.appendChild(newPerson);
// Enregistrer les modifications dans un fichier
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(new File("contacts-updated.xml")));
Principaux avantages et inconvénients de DOM
- Avantages : pratique pour les petits fichiers, facile de faire toutes sortes de modifications, on peut « naviguer » dans l’arbre dans n’importe quel sens.
- Inconvénients : tout le XML est conservé en mémoire. Pour de gros fichiers (centaines de Mo et plus) — ce n’est pas envisageable, la mémoire s’épuisera rapidement.
2. SAX (Simple API for XML)
SAX est un analyseur événementiel. Il ne construit pas d’arbre, mais lit simplement le XML « de gauche à droite » et appelle des gestionnaires d’événements : « début d’élément », « contenu texte », « fin d’élément », etc. Vous écrivez votre gestionnaire et réagissez aux événements qui vous intéressent.
Analogie :
Si DOM consiste à étaler toutes les pages d’un agenda sur la table, SAX revient à lire l’agenda page par page et à prendre des notes uniquement lorsque vous tombez sur la page voulue.
Principales classes SAX en Java
- SAXParserFactory, SAXParser — fabrique et analyseur.
- DefaultHandler — classe de base pour la gestion des événements.
- Méthodes gestionnaires : startElement, characters, endElement, startDocument, endDocument.
Exemple : lecture d’un fichier XML avec SAX
Supposons le même fichier contacts.xml. Nous voulons simplement afficher les noms et les e-mails de toutes les personnes.
Code : analyseur SAX
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
public class SaxExample {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(new File("contacts.xml"), new ContactHandler());
}
}
class ContactHandler extends DefaultHandler {
private String currentElement = "";
private String name = "";
private String email = "";
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
currentElement = qName;
if ("person".equals(qName)) {
String id = attributes.getValue("id");
System.out.println("Nouvelle personne, id: " + id);
}
}
@Override
public void characters(char[] ch, int start, int length) {
String text = new String(ch, start, length).trim();
if (text.isEmpty()) return;
if ("name".equals(currentElement)) {
name = text;
} else if ("email".equals(currentElement)) {
email = text;
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if ("person".equals(qName)) {
System.out.println("Nom: " + name + ", email: " + email);
name = "";
email = "";
}
currentElement = "";
}
}
Voyons maintenant simplement ce qui se passe ici. Lorsque l’analyseur SAX rencontre une balise ouvrante, par exemple <person>, il appelle la méthode startElement. Là, nous lisons immédiatement l’attribut id et l’affichons à l’écran. Quand du texte apparaît à l’intérieur — par exemple, un nom ou un e‑mail — l’exécution passe à la méthode characters, où nous stockons ce texte dans des variables temporaires. Et lorsque l’analyseur atteint la balise fermante </person>, endElement est appelée. À ce moment‑là, nous connaissons déjà le nom et l’e‑mail de la personne et pouvons les afficher. Ensuite, les variables sont réinitialisées pour être prêtes pour le contact suivant.
L’idée est que SAX ne conserve pas tout le XML en mémoire, mais fonctionne comme un « lecteur » en flux : il parcourt le fichier de haut en bas et réagit aux événements — début de balise, texte, fin de balise. C’est rapide et économe en mémoire, surtout pour les gros fichiers.
Comparaison rapide de DOM et SAX
| DOM | SAX | |
|---|---|---|
| Style | Arbre (toute la structure en mémoire) | Événements (traitement « à la volée ») |
| Mémoire | Charge tout le XML | Utilisation minimale de la mémoire |
| Modifications | Lecture/modification/ajout possibles | Lecture seule (en général) |
| Recherche de données | Recherche facile partout | Il faut suivre « où l’on se trouve » |
| Taille du fichier | Pour les fichiers petits à moyens | Pour les très gros fichiers |
3. Quand utiliser DOM et quand SAX
DOM convient parfaitement si :
- Le fichier est de petite ou moyenne taille.
- Vous devez accéder à plusieurs reprises à différentes parties du XML.
- Vous devez modifier la structure du document.
SAX — votre choix si :
- Le fichier est énorme et vous ne pouvez pas le charger en mémoire.
- Vous devez « extraire » rapidement seulement une partie de l’information (par exemple, trouver tous les éléments <item> avec un attribut donné).
- Vous avez besoin de hautes performances et d’une utilisation minimale de la mémoire.
- Vous ne prévoyez pas de modifier le XML, seulement de le lire.
Conseil :
Dans les projets réels, on utilise souvent les deux approches : DOM — pour des réglages « humains » et de petits fichiers de configuration, SAX — pour traiter des logs, des exports, de gros imports.
4. Exercice pratique : un petit analyseur pour une application
Dans votre application d’apprentissage (par exemple, un « carnet d’adresses »), supposons qu’une tâche apparaisse : compter rapidement le nombre d’utilisateurs avec une adresse e‑mail sur gmail.com.
Solution DOM :
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("contacts.xml"));
NodeList emails = doc.getElementsByTagName("email");
int count = 0;
for (int i = 0; i < emails.getLength(); i++) {
String email = emails.item(i).getTextContent();
if (email.endsWith("@gmail.com")) {
count++;
}
}
System.out.println("Utilisateurs avec gmail.com: " + count);
Solution SAX :
class GmailCounterHandler extends DefaultHandler {
private String currentElement = "";
int count = 0;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
currentElement = qName;
}
@Override
public void characters(char[] ch, int start, int length) {
if ("email".equals(currentElement)) {
String email = new String(ch, start, length).trim();
if (email.endsWith("@gmail.com")) {
count++;
}
}
}
@Override
public void endDocument() {
System.out.println("Utilisateurs avec gmail.com: " + count);
}
}
5. Particularités et nuances de DOM et SAX
- DOM peut saturer toute la mémoire si le fichier XML est très volumineux. Si vous voyez l’erreur OutOfMemoryError, il est probablement temps de passer à SAX.
- SAX demande de la vigilance : il faut suivre dans quel élément vous vous trouvez et assembler soigneusement les données nécessaires. Il faut parfois utiliser une pile ou des variables supplémentaires pour ne pas se perdre dans des structures profondément imbriquées.
- Dans SAX, la méthode characters peut être appelée plusieurs fois pour le même bloc de texte (surtout si le texte est long ou contient des caractères spéciaux). Il vaut mieux accumuler le texte dans un StringBuilder.
- DOM convient très bien à la recherche, à la navigation et à la modification de la structure, mais pas au traitement en flux.
- Si vous n’êtes pas sûr de l’approche à choisir — commencez par DOM pour la simplicité, et si la mémoire devient un problème — réécrivez avec SAX.
6. Erreurs typiques avec DOM et SAX
Erreur n° 1 : mauvaise gestion des espaces et retours à la ligne dans SAX.
La méthode characters peut renvoyer des fragments de texte, y compris des espaces et des retours à la ligne entre les éléments. Si vous ne filtrez pas avec .trim().isEmpty(), vous risquez d’obtenir beaucoup d’appels « vides » ou de reconstruire le texte de manière incorrecte.
Erreur n° 2 : tenter de modifier du XML avec SAX.
SAX — c’est uniquement pour la lecture ! Si vous devez modifier la structure — utilisez DOM.
Erreur n° 3 : non‑respect de l’ordre des événements dans SAX.
Si les variables ne sont pas réinitialisées dans endElement, vous pouvez obtenir une « fuite » de données entre les éléments.
Erreur n° 4 : utiliser DOM pour des fichiers énormes.
Résultat — OutOfMemoryError ou fonctionnement très lent.
Erreur n° 5 : mauvais cast de types dans DOM.
Dans DOM, tout est un Node, mais pour travailler avec les attributs et les éléments enfants, il faut caster en Element. Une erreur de cast peut entraîner une ClassCastException.
GO TO FULL VERSION