Mengubah Atribut XML di Java

1. Pengenalan

Satu aktiviti biasa ketika kita bekerja dengan XML adalah bekerja dengan atributnya. Dalam tutorial ini, kita akan meneroka cara mengubah atribut XML menggunakan Java.

2. Kebergantungan

Untuk menjalankan ujian kami, kami perlu menambahkan kebergantungan JUnit dan xmlunit-assertj ke projek Maven kami:

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

3. Menggunakan JAXP

Mari kita mulakan dengan dokumen XML:

  [email protected] [email protected] 

Untuk memprosesnya, kami akan menggunakan Java API for XML Processing (JAXP) , yang telah digabungkan dengan Java sejak versi 1.4.

Mari ubah sifat pelanggan dan ubah nilainya menjadi palsu .

Pertama, kita perlu membina objek Dokumen dari fail XML, dan untuk melakukannya, kita akan menggunakan 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);

Nota bahawa untuk melumpuhkan pemprosesan entiti luaran (XXE) untuk DocumentBuilderFactory kelas, kita mengkonfigurasi XMLConstants.FEATURE_SECURE_PROCESSING dan //apache.org/xml/features/disallow-doctype-decl ciri . Merupakan amalan yang baik untuk mengkonfigurasinya ketika kami menguraikan fail XML yang tidak dipercayai.

Setelah memulakan objek input kami , kami perlu mencari simpul dengan atribut yang ingin kami ubah. Mari gunakan ungkapan XPath untuk memilihnya:

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

Dalam kes ini, kaedah penilaian XPath mengembalikan senarai simpul dengan nod yang dipadankan.

Mari lakukan lelaran untuk menukar nilai:

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

Atau, bukannya loop for , kita boleh menggunakan IntStream :

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

Sekarang, mari kita gunakan objek Transformer untuk menerapkan perubahan:

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

Sekiranya kami mencetak isi objek output , kami akan mendapatkan XML yang dihasilkan dengan atribut pelanggan diubah:

  [email protected] [email protected] 

Juga, kita boleh menggunakan assertThat kaedah XMLUnit jika kita perlu mengesahkannya dalam ujian unit:

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

4. Menggunakan dom4j

dom4j adalah kerangka sumber terbuka untuk memproses XML yang disatukan dengan XPath dan menyokong sepenuhnya Koleksi DOM, SAX, JAXP, dan Java.

4.1. Ketergantungan Maven

Kita perlu menambahkan kebergantungan dom4j dan jaxen ke pom.xml kita untuk menggunakan dom4j dalam projek kita:

 org.dom4j dom4j 2.1.1   jaxen jaxen 1.2.0 

Kami dapat mengetahui lebih lanjut mengenai dom4j dalam artikel Sokongan Perpustakaan XML kami.

4.2. Menggunakan atribut org.dom4j.Element.addAttribute

dom4j menawarkan antara muka Elemen sebagai pengabstrakan untuk elemen XML. Kami akan menggunakan kaedah addAttribute untuk mengemas kini atribut pelanggan kami .

Mari lihat bagaimana ini berfungsi.

Pertama, kita perlu membina objek Dokumen dari fail XML - kali ini, kita akan menggunakan 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);

Kami menetapkan ciri tambahan untuk mengelakkan XXE.

Seperti JAXP, kita boleh menggunakan ekspresi XPath untuk memilih nod:

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

Sekarang, kita dapat mengulangi dan mengemas kini atribut:

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 |

Seperti yang kita lihat, untuk kes penggunaan ini dan pelaksanaannya, dom4j dan JAXP mempunyai skor yang lebih baik daripada jOOX.

7. Kesimpulannya

Dalam tutorial ringkas ini, kami telah memperkenalkan cara mengubah atribut XML menggunakan JAXP, dom4j, dan jOOX. Juga, kami mengukur prestasi perpustakaan ini dengan penanda aras JMH.

Seperti biasa, semua contoh kod yang ditunjukkan di sini boleh didapati di GitHub.