Modifica di un attributo XML in Java

1. Introduzione

Un'attività comune quando lavoriamo con XML è lavorare con i suoi attributi. In questo tutorial, esploreremo come modificare un attributo XML utilizzando Java.

2. Dipendenze

Per eseguire i nostri test, dovremo aggiungere le dipendenze JUnit e xmlunit-assertj al nostro progetto Maven:

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

3. Utilizzo di JAXP

Cominciamo con un documento XML:

  [email protected] [email protected] 

Per elaborarlo, utilizzeremo l'API Java per l'elaborazione XML (JAXP) , che è stata fornita in bundle con Java dalla versione 1.4.

Modifichiamo l' attributo del cliente e cambiamo il suo valore in false .

Per prima cosa, dobbiamo creare un oggetto Document dal file XML e per farlo useremo 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);

Si noti che, al fine di disabilitare l'elaborazione entità esterna (XXE) per il DocumentBuilderFactory di classe, configuriamo il XMLConstants.FEATURE_SECURE_PROCESSING e //apache.org/xml/features/disallow-doctype-decl caratteristiche . È buona norma configurarlo quando analizziamo file XML non attendibili.

Dopo aver inizializzato il nostro oggetto di input , dovremo individuare il nodo con l'attributo che vorremmo modificare. Usiamo un'espressione XPath per selezionarlo:

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

In questo caso, il metodo di valutazione XPath restituisce un elenco di nodi con i nodi corrispondenti.

Ripetiamo l'elenco per modificare il valore:

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

Oppure, invece di un ciclo for , possiamo usare un IntStream :

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

Ora, usiamo un oggetto Transformer per applicare le modifiche:

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));

Se stampiamo il contenuto dell'oggetto di output , otterremo l'XML risultante con l' attributo del cliente modificato:

  [email protected] [email protected] 

Inoltre, possiamo usare il metodo assertThat di XMLUnit se dobbiamo verificarlo in uno unit test:

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

4. Utilizzando dom4j

dom4j è un framework open source per l'elaborazione di XML che è integrato con XPath e supporta completamente DOM, SAX, JAXP e Java Collections.

4.1. Dipendenza da Maven

Dobbiamo aggiungere le dipendenze dom4j e jaxen al nostro pom.xml per utilizzare dom4j nel nostro progetto:

 org.dom4j dom4j 2.1.1   jaxen jaxen 1.2.0 

Possiamo saperne di più su dom4j nel nostro articolo sul supporto delle librerie XML.

4.2. Utilizzando org.dom4j.Element.addAttribute

dom4j offre l' interfaccia Element come astrazione per un elemento XML. Useremo il metodo addAttribute per aggiornare il nostro attributo cliente .

Vediamo come funziona.

Innanzitutto, dobbiamo creare un oggetto Document dal file XML: questa volta utilizzeremo 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);

Abbiamo impostato le funzionalità aggiuntive al fine di prevenire XXE.

Come JAXP, possiamo utilizzare un'espressione XPath per selezionare i nodi:

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

Ora possiamo iterare e aggiornare l'attributo:

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

Notare che con questo metodo, se esiste già un attributo per il nome dato, verrà sostituito. Altrimenti verrà aggiunto.

Per stampare i risultati, possiamo riutilizzare il codice della precedente sezione JAXP.

5. Utilizzo di jOOX

jOOX (jOOX Object-Oriented XML) è un wrapper per il pacchetto org.w3c.dom che consente la creazione e la manipolazione fluide di documenti XML dove DOM è richiesto ma troppo dettagliato. jOOX avvolge solo il documento sottostante e può essere utilizzato per migliorare DOM, non come alternativa.

5.1. Dipendenza da Maven

Dobbiamo aggiungere la dipendenza al nostro pom.xml per utilizzare jOOX nel nostro progetto.

Per l'utilizzo con Java 9+, possiamo utilizzare:

 org.jooq joox 1.6.2 

O con Java 6+, abbiamo:

 org.jooq joox-java-6 1.6.2 

Possiamo trovare le ultime versioni di joox e joox-java-6 nel repository 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 |

Come possiamo vedere, per questo caso d'uso e la nostra implementazione, dom4j e JAXP hanno punteggi migliori di jOOX.

7. Conclusione

In questo breve tutorial, abbiamo introdotto come modificare gli attributi XML utilizzando JAXP, dom4j e jOOX. Inoltre, abbiamo misurato le prestazioni di queste librerie con un benchmark JMH.

Come al solito, tutti gli esempi di codice mostrati qui sono disponibili su GitHub.