Ändern eines XML-Attributs in Java

1. Einleitung

Eine häufige Aktivität bei der Arbeit mit XML ist die Arbeit mit seinen Attributen. In diesem Tutorial erfahren Sie, wie Sie ein XML-Attribut mit Java ändern.

2. Abhängigkeiten

Um unsere Tests ausführen zu können, müssen wir unserem Maven-Projekt die Abhängigkeiten JUnit und xmlunit-assertj hinzufügen :

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

3. Verwenden von JAXP

Beginnen wir mit einem XML-Dokument:

  [email protected] [email protected] 

Zur Verarbeitung verwenden wir die Java-API für die XML-Verarbeitung (JAXP) , die seit Version 1.4 mit Java gebündelt ist.

Lassen Sie uns das ändern Kunden Attribut und seinen Wert ändern falsch .

Zuerst müssen wir ein Document- Objekt aus der XML-Datei erstellen, und dazu verwenden wir eine 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);

Beachten Sie, dass , um zu deaktivieren externe Einheit Verarbeitung (XXE) für die Document Klasse, wir konfigurieren XMLConstants.FEATURE_SECURE_PROCESSING und //apache.org/xml/features/disallow-doctype-decl Funktionen . Es wird empfohlen, es zu konfigurieren, wenn nicht vertrauenswürdige XML-Dateien analysiert werden.

Nach der Initialisierung unseres Eingabeobjekts müssen wir den Knoten mit dem Attribut suchen, das wir ändern möchten. Verwenden wir einen XPath-Ausdruck, um ihn auszuwählen:

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

In diesem Fall gibt die XPath- Auswertungsmethode eine Knotenliste mit den übereinstimmenden Knoten zurück.

Lassen Sie uns die Liste durchlaufen, um den Wert zu ändern:

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

Oder wir können anstelle einer for- Schleife einen IntStream verwenden :

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

Verwenden wir nun ein Transformer- Objekt, um die Änderungen zu übernehmen:

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

Wenn wir den Druck Ausgabe Objektinhalt, werden wir die daraus resultierenden XML mit dem erhaltenen Kunden Attribute geändert:

  [email protected] [email protected] 

Wir können auch die assertThat- Methode von XMLUnit verwenden, wenn wir sie in einem Komponententest überprüfen müssen:

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

4. Verwenden von dom4j

dom4j ist ein Open-Source-Framework für die Verarbeitung von XML, das in XPath integriert ist und DOM-, SAX-, JAXP- und Java-Sammlungen vollständig unterstützt.

4.1. Maven-Abhängigkeit

Wir müssen die Abhängigkeiten dom4j und jaxen zu unserer pom.xml hinzufügen , um dom4j in unserem Projekt verwenden zu können:

 org.dom4j dom4j 2.1.1   jaxen jaxen 1.2.0 

Weitere Informationen zu dom4j finden Sie in unserem Artikel zur Unterstützung von XML-Bibliotheken.

4.2. Verwenden von org.dom4j.Element.addAttribute

dom4j bietet die Elementschnittstelle als Abstraktion für ein XML-Element an. Wir werden die unter Verwendung addAttribute Methode , um unsere aktualisieren Kunden Attribut.

Mal sehen, wie das funktioniert.

Zuerst müssen wir ein Dokumentobjekt aus der XML-Datei erstellen - dieses Mal verwenden wir einen 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);

Wir stellen die zusätzlichen Funktionen ein, um XXE zu verhindern.

Wie bei JAXP können wir einen XPath-Ausdruck verwenden, um die Knoten auszuwählen:

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

Jetzt können wir das Attribut iterieren und aktualisieren:

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

Note that with this method, if an attribute already exists for the given name, it will be replaced. Otherwise, it'll be added.

In order to print the results, we can reuse the code from the previous JAXP section.

5. Using jOOX

jOOX (jOOX Object-Oriented XML) is a wrapper for the org.w3c.dom package that allows for fluent XML document creation and manipulation where DOM is required but too verbose. jOOX only wraps the underlying document and can be used to enhance DOM, not as an alternative.

5.1. Maven Dependency

We need to add the dependency to our pom.xml to use jOOX in our project.

For use with Java 9+, we can use:

 org.jooq joox 1.6.2 

Or with Java 6+, we have:

 org.jooq joox-java-6 1.6.2 

We can find the latest versions of joox and joox-java-6 in the Maven Central repository.

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 |

Wie wir sehen können, haben dom4j und JAXP für diesen Anwendungsfall und unsere Implementierung bessere Ergebnisse als jOOX.

7. Fazit

In diesem kurzen Tutorial haben wir vorgestellt, wie Sie XML-Attribute mit JAXP, dom4j und jOOX ändern. Außerdem haben wir die Leistung dieser Bibliotheken mit einem JMH-Benchmark gemessen.

Wie üblich sind alle hier gezeigten Codebeispiele auf GitHub verfügbar.