Modification d'un attribut XML en Java

1. Introduction

Une activité courante lorsque nous travaillons avec XML est de travailler avec ses attributs. Dans ce didacticiel, nous allons explorer comment modifier un attribut XML à l'aide de Java.

2. Dépendances

Afin d'exécuter nos tests, nous devrons ajouter les dépendances JUnit et xmlunit-assertj à notre projet Maven:

 org.junit.jupiter junit-jupiter 5.5.0 test 
 org.xmlunit xmlunit-assertj 2.6.3 test 

3. Utilisation de JAXP

Commençons par un document XML:

  [email protected] [email protected] 

Pour le traiter, nous utiliserons l'API Java pour le traitement XML (JAXP) , qui est fournie avec Java depuis la version 1.4.

Modifions l' attribut client et changeons sa valeur en false .

Tout d'abord, nous devons créer un objet Document à partir du fichier XML, et pour ce faire, nous utiliserons un DocumentBuilderFactory :

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); factory.setFeature("//apache.org/xml/features/disallow-doctype-decl", true); Document input = factory .newDocumentBuilder() .parse(resourcePath);

Notez que pour désactiver le traitement de l' entité externe (XXE) pour la DocumentBuilderFactory classe, nous configurons le XMLConstants.FEATURE_SECURE_PROCESSING et //apache.org/xml/features/disallow-doctype-decl fonctionnalités . Il est recommandé de le configurer lorsque nous analysons des fichiers XML non approuvés.

Après avoir initialisé notre objet d' entrée , nous devrons localiser le nœud avec l'attribut que nous aimerions changer. Utilisons une expression XPath pour le sélectionner:

XPath xpath = XPathFactory .newInstance() .newXPath(); String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue); NodeList nodes = (NodeList) xpath.evaluate(expr, input, XPathConstants.NODESET);

Dans ce cas, la méthode d' évaluation XPath nous renvoie une liste de nœuds avec les nœuds correspondants.

Parcourons la liste pour changer la valeur:

for (int i = 0; i < nodes.getLength(); i++) { Element value = (Element) nodes.item(i); value.setAttribute(attribute, newValue); }

Ou, au lieu d'une boucle for , nous pouvons utiliser un IntStream :

IntStream .range(0, nodes.getLength()) .mapToObj(i -> (Element) nodes.item(i)) .forEach(value -> value.setAttribute(attribute, newValue));

Maintenant, utilisons un objet Transformer pour appliquer les modifications:

TransformerFactory factory = TransformerFactory.newInstance(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); Transformer xformer = factory.newTransformer(); xformer.setOutputProperty(OutputKeys.INDENT, "yes"); Writer output = new StringWriter(); xformer.transform(new DOMSource(input), new StreamResult(output));

Si nous imprimons le contenu de l'objet de sortie , nous obtiendrons le XML résultant avec l' attribut client modifié:

  [email protected] [email protected] 

De plus, nous pouvons utiliser la méthode assertThat de XMLUnit si nous devons la vérifier dans un test unitaire:

assertThat(output.toString()).hasXPath("//*[contains(@customer, 'false')]");

4. Utilisation de dom4j

dom4j est un framework open-source pour le traitement de XML qui est intégré à XPath et prend entièrement en charge les collections DOM, SAX, JAXP et Java.

4.1. Dépendance de Maven

Nous devons ajouter les dépendances dom4j et jaxen à notre pom.xml pour utiliser dom4j dans notre projet:

 org.dom4j dom4j 2.1.1   jaxen jaxen 1.2.0 

Nous pouvons en savoir plus sur dom4j dans notre article sur le support des bibliothèques XML.

4.2. Utilisation de org.dom4j.Element.addAttribute

dom4j propose l' interface Element comme abstraction pour un élément XML. Nous utiliserons la méthode addAttribute pour mettre à jour notre attribut client .

Voyons comment cela fonctionne.

Tout d'abord, nous devons créer un objet Document à partir du fichier XML - cette fois, nous utiliserons un SAXReader :

SAXReader xmlReader = new SAXReader(); Document input = xmlReader.read(resourcePath); xmlReader.setFeature("//apache.org/xml/features/disallow-doctype-decl", true); xmlReader.setFeature("//xml.org/sax/features/external-general-entities", false); xmlReader.setFeature("//xml.org/sax/features/external-parameter-entities", false);

Nous définissons les fonctionnalités supplémentaires afin d'éviter XXE.

Comme JAXP, nous pouvons utiliser une expression XPath pour sélectionner les nœuds:

String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue); XPath xpath = DocumentHelper.createXPath(expr); List nodes = xpath.selectNodes(input);

Maintenant, nous pouvons itérer et mettre à jour l'attribut:

for (int i = 0; i < nodes.size(); i++) { Element element = (Element) nodes.get(i); element.addAttribute(attribute, newValue); }

Notez qu'avec cette méthode, si un attribut existe déjà pour le nom donné, il sera remplacé. Sinon, il sera ajouté.

Afin d'imprimer les résultats, nous pouvons réutiliser le code de la section JAXP précédente.

5. Utilisation de jOOX

jOOX (jOOX Object-Oriented XML) est un wrapper pour le package org.w3c.dom qui permet la création et la manipulation de documents XML fluides là où DOM est requis mais trop verbeux. jOOX encapsule uniquement le document sous-jacent et peut être utilisé pour améliorer DOM, pas comme alternative.

5.1. Dépendance de Maven

Nous devons ajouter la dépendance à notre pom.xml pour utiliser jOOX dans notre projet.

Pour une utilisation avec Java 9+, nous pouvons utiliser:

 org.jooq joox 1.6.2 

Ou avec Java 6+, nous avons:

 org.jooq joox-java-6 1.6.2 

Nous pouvons trouver les dernières versions de joox et joox-java-6 dans le référentiel Maven Central.

5.2. Using org.w3c.dom.Element.setAttribute

The jOOX API itself is inspired by jQuery, as we can see in the examples below. Let's see how to use it.

First, we need to load the Document:

DocumentBuilder builder = JOOX.builder(); Document input = builder.parse(resourcePath);

Now, we need to select it:

Match $ = $(input);

In order to select the customer Element, we can use the find method or an XPath expression. In both cases, we'll get a list of the elements that match it.

Let's see the find method in action:

$.find("to") .get() .stream() .forEach(e -> e.setAttribute(attribute, newValue));

To get the result as a String, we simply need to call the toString() method:

$.toString();

6. Benchmark

In order to compare the performance of these libraries, we used a JMH benchmark.

Let's see the results:

| Benchmark Mode Cnt Score Error Units | |--------------------------------------------------------------------| | AttributeBenchMark.dom4jBenchmark avgt 5 0.150 ± 0.003 ms/op | | AttributeBenchMark.jaxpBenchmark avgt 5 0.166 ± 0.003 ms/op | | AttributeBenchMark.jooxBenchmark avgt 5 0.230 ± 0.033 ms/op |

Comme nous pouvons le voir, pour ce cas d'utilisation et notre implémentation, dom4j et JAXP ont de meilleurs scores que jOOX.

7. Conclusion

Dans ce tutoriel rapide, nous avons présenté comment modifier les attributs XML à l'aide de JAXP, dom4j et jOOX. De plus, nous avons mesuré les performances de ces bibliothèques avec un benchmark JMH.

Comme d'habitude, tous les exemples de code présentés ici sont disponibles à l'adresse over sur GitHub.