Een XML-kenmerk in Java wijzigen

1. Inleiding

Een veel voorkomende activiteit wanneer we met XML werken, is het werken met zijn attributen. In deze zelfstudie onderzoeken we hoe u een XML-kenmerk kunt wijzigen met Java.

2. Afhankelijkheden

Om onze tests uit te voeren, moeten we de JUnit en xmlunit-assertj afhankelijkheden van ons Maven-project:

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

3. JAXP gebruiken

Laten we beginnen met een XML-document:

  [e-mail beschermd] [e-mail beschermd] 

Om het te verwerken, zullen we gebruik de Java API voor XML-verwerking (JAXP), die sinds versie 1.4 met Java is gebundeld.

Laten we het klant attribuut en verander de waarde ervan in false.

Eerst moeten we een Document object uit het XML-bestand, en om dat te doen, gebruiken we een DocumentBuilderFactory:

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

Merk op dat om externe entiteitsverwerking uitschakelen (XXE) voor de DocumentBuilderFactory klasse, we configureren de XMLConstants.FEATURE_SECURE_PROCESSING en //apache.org/xml/features/disallow-doctype-decl Kenmerken. Het is een goede gewoonte om het te configureren wanneer we niet-vertrouwde XML-bestanden parseren.

Na het initialiseren van onze invoer object, moeten we het knooppunt lokaliseren met het attribuut dat we willen wijzigen. Laten we een XPath-expressie gebruiken om deze te selecteren:

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

In dit geval is het XPath evalueren methode geeft ons een knooppuntlijst met de overeenkomende knooppunten.

Laten we de lijst herhalen om de waarde te wijzigen:

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

Of, in plaats van een voor loop, we kunnen een IntStream:

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

Laten we nu een Transformator bezwaar maken om de wijzigingen toe te passen:

TransformerFactory factory = TransformerFactory.newInstance (); factory.setFeature (XMLConstants.FEATURE_SECURE_PROCESSING, true); Transformator xformer = factory.newTransformer (); xformer.setOutputProperty (OutputKeys.INDENT, "ja"); Writer output = nieuwe StringWriter (); xformer.transform (nieuwe DOMSource (invoer), nieuwe StreamResult (uitvoer));

Als we het output objectinhoud, krijgen we de resulterende XML met de klant attribuut gewijzigd:

  [e-mail beschermd] [e-mail beschermd] 

We kunnen ook de beweren dat methode van XMLUnit als we het moeten verifiëren in een unit-test:

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

4. Met behulp van dom4j

dom4j is een open-source framework voor het verwerken van XML dat is geïntegreerd met XPath en DOM, SAX, JAXP en Java Collections volledig ondersteunt.

4.1. Afhankelijkheid van Maven

We moeten de dom4j- en jaxen-afhankelijkheden toevoegen aan onze pom.xml dom4j gebruiken in ons project:

 org.dom4j dom4j 2.1.1 jaxen jaxen 1.2.0 

We kunnen meer te weten komen over dom4j in ons artikel over XML Libraries Support.

4.2. Gebruik makend van org.dom4j.Element.addAttribute

dom4j biedt de Element interface als abstractie voor een XML-element. We gebruiken de attribuut toevoegen methode om onze klant attribuut.

Laten we eens kijken hoe dit werkt.

Eerst moeten we een Document object uit het XML-bestand - deze keer gebruiken we een SAXReader:

SAXReader xmlReader = nieuwe SAXReader (); Documentinvoer = 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);

We stellen de extra functies in om XXE te voorkomen.

Net als JAXP kunnen we een XPath-expressie gebruiken om de knooppunten te selecteren:

String expr = String.format ("// * [bevat (@% s, '% s')]", attribuut, oldValue); XPath xpath = DocumentHelper.createXPath (expr); Lijstknooppunten = xpath.selectNodes (invoer);

Nu kunnen we het kenmerk herhalen en bijwerken:

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

Merk op dat bij deze methode, als er al een attribuut bestaat voor de opgegeven naam, deze zal worden vervangen. Anders wordt het toegevoegd.

Om de resultaten af ​​te drukken, kunnen we de code uit de vorige JAXP-sectie hergebruiken.

5. jOOX gebruiken

jOOX (jOOX Object-Oriented XML) is een wrapper voor de org.w3c.dom pakket dat vloeiend XML-documentcreatie en -manipulatie mogelijk maakt waar DOM vereist is, maar te uitgebreid. jOOX verpakt alleen het onderliggende document en kan worden gebruikt om DOM te verbeteren, niet als alternatief.

5.1. Afhankelijkheid van Maven

We moeten de afhankelijkheid toevoegen aan onze pom.xml om jOOX in ons project te gebruiken.

Voor gebruik met Java 9+ kunnen we gebruiken:

 org.jooq joox 1.6.2 

Of met Java 6+ hebben we:

 org.jooq joox-java-6 1.6.2 

We kunnen de nieuwste versies van joox en joox-java-6 in de Maven Central-opslagplaats.

5.2. Gebruik makend van org.w3c.dom.Element.setAttribute

De jOOX API zelf is geïnspireerd door jQuery, zoals we kunnen zien in de onderstaande voorbeelden. Laten we eens kijken hoe we het kunnen gebruiken.

Eerst moeten we het Document:

DocumentBuilder-builder = JOOX.builder (); Documentinvoer = builder.parse (resourcePath);

Nu moeten we het selecteren:

Match $ = $ (invoer);

Om het klant Element, we kunnen de vind methode of een XPath-expressie. In beide gevallen krijgen we een lijst met de elementen die ermee overeenkomen.

Laten we eens kijken naar de vind methode in actie:

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

Om het resultaat te krijgen als een Draad, we hoeven alleen maar de toString () methode:

$ .toString ();

6. Benchmark

Om de prestaties van deze bibliotheken te vergelijken, hebben we een JMH-benchmark gebruikt.

Laten we de resultaten bekijken:

| Benchmarkmodus Cnt Score Fouteenheden | | ------------------------------------------------- ------------------- | | AttributeBenchMark.dom4jBenchmark avgt 5 0,150 ± 0,003 ms / op | | AttributeBenchMark.jaxpBenchmark avgt 5 0,166 ± 0,003 ms / op | | AttributeBenchMark.jooxBenchmark gem. 5 0,230 ± 0,033 ms / op |

Zoals we kunnen zien, scoren dom4j en JAXP voor deze use case en onze implementatie beter dan jOOX.

7. Conclusie

In deze korte tutorial hebben we geïntroduceerd hoe u XML-attributen kunt wijzigen met JAXP, dom4j en jOOX. We hebben ook de prestaties van deze bibliotheken gemeten met een JMH-benchmark.

Zoals gewoonlijk zijn alle hier getoonde codevoorbeelden beschikbaar op GitHub.