W3docs

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.

ConceptInterfaceCe qu'il représente
DocumentDocumentLe fichier analysé en entier ; point d'entrée de l'arbre
ÉlémentElementUne balise comme <book>, avec attributs et enfants
AttributAttrUne paire nom/valeur sur un élément
TexteTextDonnées caractères à l'intérieur d'un élément
Liste de nœudsNodeListUne 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.

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.

java— editable, runs on the server

Ce qu'il faut retenir de l'exécution :

  • L'élément racine s'affiche comme library parce que getDocumentElement() 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 avec Double.parseDouble, donc 45.00 et 38.50 s'additionnent pour donner le total affiché de 83.50.
  • Après appendChild, la même requête getElementsByTagName("book") renvoie 3, montrant que l'arbre en direct a pris en compte le nœud créé avec doc.createElement.
  • La première ligne de prix sérialisée affiche 49.50, prouvant que setTextContent a muté le nœud en mémoire et que le Transformer a écrit la valeur mise à jour (45.00 augmentée de 10%) en XML.

Pratique

Pratique
Dans l'API DOM, pourquoi doit-on appeler doc.createElement() avant appendChild() pour ajouter un nouveau noeud ?
Dans l'API DOM, pourquoi doit-on appeler doc.createElement() avant appendChild() pour ajouter un nouveau noeud ?
Was this page helpful?