Parseur XML DOM en Java
Apprenez à analyser, naviguer, modifier et sérialiser du XML en Java avec le parseur DOM intégré, avec un exemple complet lecture-modification-écriture.
Le parseur DOM (Document Object Model) lit un document XML entier en mémoire et vous fournit un arbre de nœuds que vous pouvez parcourir, interroger et modifier. Il est fourni avec le JDK dans javax.xml.parsers et org.w3c.dom, il n'y a donc rien à ajouter à votre classpath. DOM est l'outil approprié lorsque les documents sont assez petits pour tenir en mémoire et que vous avez besoin d'un accès aléatoire à n'importe quelle partie de l'arbre — lecture d'un fichier de configuration, transformation d'une charge utile, ou construction de XML par programmation.
Ce chapitre parcourt le cycle de vie complet : comment DOM modélise un document, comment analyser une source en arbre, comment lire et modifier des nœuds, et comment réécrire l'arbre en XML. Si vous débutez avec XML en Java, commencez par l'introduction à XML ; pour les documents volumineux où la mémoire est un enjeu, comparez DOM avec le parseur SAX en flux.
Comment DOM modélise un document
DOM transforme le balisage en un arbre d'objets Node. Chaque élément, attribut, morceau de texte et commentaire est un nœud, et l'ensemble du document est suspendu à une racine Document unique. Vous lisez l'arbre en demandant à des nœuds leurs enfants, et vous le modifiez en créant, déplaçant ou supprimant des nœuds.
| Concept | Interface | Ce qu'il représente |
|---|---|---|
| Document | Document | Le fichier analysé en entier ; point d'entrée de l'arbre |
| Élément | Element | Une balise comme <book>, avec attributs et enfants |
| Attribut | Attr | Une paire nom/valeur sur un élément |
| Texte | Text | Données caractères à l'intérieur d'un élément |
| Liste de nœuds | NodeList | Une collection de nœuds ordonnée et adressable par index |
Le principal compromis : DOM est pratique car tout l'arbre est adressable, mais il charge tout en mémoire à la fois. Pour des flux de plusieurs gigaoctets, vous vous tourneriez vers SAX ou StAX à la place, qui diffusent le document sans construire d'arbre. Et si vous mappez du XML vers et depuis des objets Java plutôt que de parcourir des nœuds bruts, JAXB nécessite généralement moins de code que du DOM écrit à la main.
Analyser un document
Vous ne construisez jamais un parseur directement. Vous demandez à un DocumentBuilderFactory un DocumentBuilder, puis vous appelez parse sur un flux, un fichier ou un URI. Configurez la factory avant de construire — la prise en charge des espaces de noms et la validation sont des paramètres au niveau de la factory.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("library.xml"));
// Collapse adjacent text nodes and drop empty ones so getTextContent is clean.
doc.getDocumentElement().normalize();parse lève SAXException pour du XML malformé et IOException si la source ne peut pas être lue, ce sont donc deux exceptions vérifiées que vous devez gérer. Appeler normalize() une fois après l'analyse fusionne les nœuds texte fractionnés — une source courante de surprises lors de la lecture du texte d'un élément.
Naviguer dans l'arbre
Deux méthodes couvrent la plupart des lectures : getElementsByTagName trouve tous les descendants ayant un tag donné, et getChildNodes renvoie les enfants directs d'un nœud. N'oubliez pas que getChildNodes inclut les nœuds texte d'espacement, donc filtrez par type de nœud lorsque vous ne voulez que des éléments.
Element root = doc.getDocumentElement(); // <library>
NodeList books = doc.getElementsByTagName("book"); // every <book> in the tree
for (int i = 0; i < books.getLength(); i++) {
Element book = (Element) books.item(i);
String id = book.getAttribute("id"); // attribute by name
String title = book.getElementsByTagName("title")
.item(0).getTextContent(); // first child <title> text
System.out.println(id + " -> " + title);
}NodeList est basé sur des index et n'est pas itérable, donc vous bouclez avec getLength() et item(i). getAttribute renvoie une chaîne vide (jamais null) lorsque l'attribut est absent, ce qui vaut la peine de savoir avant d'écrire une vérification null qui ne se déclenche jamais.
Modifier et créer des nœuds
L'arbre DOM est mutable. Vous changez le texte avec setTextContent, changez les attributs avec setAttribute, et développez l'arbre en créant des nœuds via les méthodes factory de Document et en les ajoutant. Les nœuds doivent être créés par le même document dans lequel ils sont insérés.
// Update existing content.
Element price = (Element) book.getElementsByTagName("price").item(0);
price.setTextContent("49.50");
price.setAttribute("currency", "USD");
// Build a new subtree and attach it.
Element added = doc.createElement("book");
added.setAttribute("id", "b3");
Element title = doc.createElement("title");
title.setTextContent("The Pragmatic Programmer");
added.appendChild(title);
doc.getDocumentElement().appendChild(added);createElement crée un nœud détaché ; rien n'apparaît dans le document tant que vous ne l'avez pas ajouté quelque part avec appendChild. Pour supprimer un nœud, appelez parent.removeChild(child).
Réécrire l'arbre en XML
DOM n'a pas de toString() qui produit du XML. Pour sérialiser, passez le document à un Transformer avec un DOMSource et un StreamResult. Le même package javax.xml.transform vous permet d'écrire dans un fichier, une chaîne ou n'importe quel flux, et de définir des options de mise en forme.
Transformer tr = TransformerFactory.newInstance().newTransformer();
tr.setOutputProperty(OutputKeys.INDENT, "yes");
tr.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
tr.transform(new DOMSource(doc), new StreamResult(new File("out.xml")));Pour les entrées non fiables, renforcez la factory avant l'analyse — désactivez les déclarations DOCTYPE avec factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) pour bloquer les attaques XXE (XML External Entity).
Un exemple complet
Ce programme analyse un document de bibliothèque en mémoire, lit chaque livre, augmente chaque prix de 10%, insère un nouveau livre, et sérialise la première ligne de prix mise à jour en XML — exerçant le cycle complet lecture-modification-écriture sur un seul arbre.
Ce qu'il faut retenir de l'exécution :
- L'élément racine s'affiche comme
libraryparce quegetDocumentElement()renvoie le seul nœud du sommet auquel tout le reste est accroché. getElementsByTagName("book")signale un nombre de 2 avant l'insertion, confirmant qu'il a collecté les deux descendants<book>de la racine.- Les prix sont lus avec
getTextContent()et analysés avecDouble.parseDouble, donc45.00et38.50s'additionnent pour donner le total affiché de83.50. - Après
appendChild, la même requêtegetElementsByTagName("book")renvoie 3, montrant que l'arbre en direct a pris en compte le nœud créé avecdoc.createElement. - La première ligne de prix sérialisée affiche
49.50, prouvant quesetTextContenta muté le nœud en mémoire et que leTransformera écrit la valeur mise à jour (45.00 augmentée de 10%) en XML.