Java >> Java tutorial >  >> Tag >> XML

Ændring af en XML-attribut i Java

1. Introduktion

En almindelig aktivitet, når vi arbejder med XML, er at arbejde med dens attributter. I denne øvelse vil vi undersøge, hvordan man ændrer en XML-attribut ved hjælp af Java.

2. Afhængigheder

For at køre vores tests skal vi tilføje JUnit og xmlunit-assertj afhængigheder til vores Maven-projekt:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.8.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.xmlunit</groupId>
    <artifactId>xmlunit-assertj</artifactId>
    <version>2.6.3</version>
    <scope>test</scope>
</dependency>

3. Bruger JAXP

Lad os starte med et XML-dokument:

<?xml version="1.0" encoding="UTF-8"?>
<notification id="5">
    <to customer="true">[email protected]</to>
    <from>[email protected]</from>
</notification>

For at behandle det, skal vi bruge Java API til XML-behandling (JAXP) , som har været bundtet med Java siden version 1.4.

Lad os ændre kunden attribut og ændre dens værdi til false .

Først skal vi bygge et Dokument objekt fra XML-filen, og for at gøre det bruger vi en DocumentBuilderFactory :

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

Bemærk, at for at deaktivere ekstern enhedsbehandling (XXE) til DocumentBuilderFactory klasse, konfigurerer vi XMLConstants.FEATURE_SECURE_PROCESSING og http://apache.org/xml/features/disallow-doctype-decl funktioner . Det er en god praksis at konfigurere det, når vi analyserer XML-filer, der ikke er tillid til.

Efter initialisering af vores input objekt, bliver vi nødt til at finde noden med den attribut, vi gerne vil ændre. Lad os bruge et XPath-udtryk til at vælge det:

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

I dette tilfælde evaluerer XPath metoden returnerer os en nodeliste med de matchede noder.

Lad os gentage listen for at ændre værdien:

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

Eller i stedet for et for loop, kan vi bruge en IntStream :

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

Lad os nu bruge en Transformer gøre indsigelse for at anvende ændringerne:

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

Hvis vi udskriver output objektindhold, får vi den resulterende XML med kunden attribut ændret:

<?xml version="1.0" encoding="UTF-8"?>
<notification id="5">
    <to customer="false">[email protected]</to>
    <from>[email protected]</from>
</notification>

Vi kan også bruge assertThat metode for XMLUnit, hvis vi skal verificere det i en enhedstest:

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

4. Brug af dom4j

dom4j er en open source-ramme til behandling af XML, der er integreret med XPath og fuldt ud understøtter DOM, SAX, JAXP og Java Collections.

4.1. Maven Dependency

Vi skal tilføje dom4j- og jaxen-afhængighederne til vores pom.xml at bruge dom4j i vores projekt:

<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.2.0</version>
</dependency>

Vi kan lære mere om dom4j i vores XML Libraries Support-artikel.

4.2. Brug af org.dom4j.Element.addAttribute

dom4j tilbyder Element interface som en abstraktion for et XML-element. Vi bruger addAttribute metode til at opdatere vores kunde attribut.

Lad os se, hvordan det fungerer.

Først skal vi bygge et Dokument objekt fra XML-filen — denne gang bruger vi en SAXReader :

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

Vi indstiller de ekstra funktioner for at forhindre XXE.

Ligesom JAXP kan vi bruge et XPath-udtryk til at vælge noderne:

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

Nu kan vi iterere og opdatere attributten:

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

Bemærk, at med denne metode, hvis der allerede findes en attribut for det givne navn, vil den blive erstattet. Ellers bliver det tilføjet.

For at udskrive resultaterne kan vi genbruge koden fra den forrige JAXP-sektion.

5. Brug af jOOX

jOOX (jOOX objektorienteret XML) er en indpakning til org.w3c.dom pakke, der giver mulighed for flydende XML-dokumentoprettelse og -manipulation, hvor DOM er påkrævet, men for omfattende. jOOX ombryder kun det underliggende dokument og kan bruges til at forbedre DOM, ikke som et alternativ.

5.1. Maven Dependency

Vi skal tilføje afhængigheden til vores pom.xml at bruge jOOX i vores projekt.

Til brug med Java 9+ kan vi bruge:

<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>joox</artifactId>
    <version>1.6.2</version>
</dependency>

Eller med Java 6+ har vi:

<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>joox-java-6</artifactId>
    <version>1.6.2</version>
</dependency>

Vi kan finde de seneste versioner af joox og joox-java-6 i Maven Central-depotet.

5.2. Brug af org.w3c.dom.Element.setAttribute

Selve jOOX API'et er inspireret af jQuery, som vi kan se i eksemplerne nedenfor. Lad os se, hvordan du bruger det.

Først skal vi indlæse Dokumentet :

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

Nu skal vi vælge det:

Match $ = $(input);

For at vælge kundeelementet vi kan bruge find metode eller et XPath-udtryk. I begge tilfælde får vi en liste over de elementer, der matcher den.

Lad os se findet metode i aktion:

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

For at få resultatet som en streng , skal vi blot kalde toString() metode:

$.toString();

6. Benchmark

For at sammenligne ydeevnen af ​​disse biblioteker brugte vi et JMH-benchmark.

Lad os se resultaterne:

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

Som vi kan se, har dom4j og JAXP bedre score end jOOX for denne anvendelse og vores implementering.

7. Konklusion

I denne hurtige vejledning har vi introduceret, hvordan man ændrer XML-attributter ved hjælp af JAXP, dom4j og jOOX. Vi målte også ydeevnen af ​​disse biblioteker med et JMH-benchmark.

Som sædvanlig er alle kodeeksemplerne vist her tilgængelige på GitHub.


Java tag