JavaでのXML属性の変更

1. 前書き

XMLを扱うときの一般的なアクティビティの1つは、その属性を扱うことです。 このチュートリアルでは、Javaを使用してXML属性を変更する方法を探ります。

2. 依存関係

テストを実行するには、https://search.maven.org/search?q = g:org.junit.jupiter%20AND%20a:junit-jupiter [JUnit]および_https:/を追加する必要があります。 /search.maven.org/search?q=g:org.xmlunit%20AND%20a:xmlunit-assertj[xmlunit-assertj]_ link:/maven[Maven]プロジェクトへの依存関係:
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.5.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.xmlunit</groupId>
    <artifactId>xmlunit-assertj</artifactId>
    <version>2.6.3</version>
    <scope>test</scope>
</dependency>

*3. JAXP *の使用

XMLドキュメントから始めましょう。
<?xml version="1.0" encoding="UTF-8"?>
<notification id="5">
    <to customer="true">[email protected]</to>
    <from>[email protected]</from>
</notification>
それを処理するために、バージョン1.4以降JavaにバンドルされているXML処理用のJava API(JAXP)*を使用します。
_customer_属性を変更して、その値を_false_に変更しましょう。
最初に、XMLファイルから_Document_オブジェクトを作成する必要があります。それを行うには、_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);
_DocumentBuilderFactory_クラスのhttps://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing [外部エンティティ処理(XXE)] *を無効にするには、* _ XMLConstants.FEATURE_SECURE_PROCESSING_および_httpを構成します。 //apache.org/xml/features/disallow-doctype-decl_ features *。 信頼できないXMLファイルを解析するときに設定することをお勧めします。
_input_オブジェクトを初期化した後、変更したい属性を持つノードを見つける必要があります。 link:/java-xpath[XPath expression]を使用して選択してみましょう。
XPath xpath = XPathFactory
  .newInstance()
  .newXPath();
String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue);
NodeList nodes = (NodeList) xpath.evaluate(expr, input, XPathConstants.NODESET);
この場合、XPath _evaluate_メソッドは、一致したノードを含むノードリストを返します。
リストを繰り返して値を変更してみましょう。
for (int i = 0; i < nodes.getLength(); i++) {
    Element value = (Element) nodes.item(i);
    value.setAttribute(attribute, newValue);
}
または、_for_ループの代わりに、_IntStream_を使用できます。
IntStream
    .range(0, nodes.getLength())
    .mapToObj(i -> (Element) nodes.item(i))
    .forEach(value -> value.setAttribute(attribute, newValue));
次に、_Transformer_オブジェクトを使用して変更を適用します。
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));
_output_オブジェクトのコンテンツを出力すると、_customer_属性が変更された結果のXMLが取得されます。
<?xml version="1.0" encoding="UTF-8"?>
<notification id="5">
    <to customer="false">[email protected]</to>
    <from>[email protected]</from>
</notification>
また、ユニットテストで検証する必要がある場合は、https://www.baeldung.com/xmlunit2 [XMLUnit]の_assertThat_メソッドを使用できます。
assertThat(output.toString()).hasXPath("//*[contains(@customer, 'false')]");

*4. dom4j *を使用する

https://dom4j.github.io/[dom4j]は、XPathと統合され、DOM、SAX、JAXP、およびJavaコレクションを完全にサポートするXMLを処理するためのオープンソースフレームワークです。

* 4.1。 Mavenの依存関係*

https://search.maven.org/search?q=g:org.dom4j%20AND%20a:dom4j[dom4j]およびhttps://search.maven.org/search?q=gを追加する必要があります。 jaxen%20AND%20a:jaxen [jaxen] _pom.xml_への依存関係は、プロジェクトでdom4jを使用します。
<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>
link:/java-xml-libraries#DOM4J[XMLライブラリサポート記事でdom4jの詳細をご覧ください]。

* 4.2。 org.dom4j.Element.addAttribute *の使用

dom4jは、XML要素の抽象化として_Element_インターフェイスを提供します。 __Attribute__メソッドを使用して、顧客属性を更新します。
これがどのように機能するかを見てみましょう。
最初に、XMLファイルから_Document_オブジェクトを作成する必要があります—今回は、_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);
XXEを防ぐために追加機能を設定します。
JAXPと同様に、XPath式を使用してノードを選択できます。
String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue);
XPath xpath = DocumentHelper.createXPath(expr);
List<Node> nodes = xpath.selectNodes(input);
これで、属性を繰り返し更新できます。
for (int i = 0; i < nodes.size(); i++) {
    Element element = (Element) nodes.get(i);
    element.addAttribute(attribute, newValue);
}
このメソッドでは、指定された名前の属性がすでに存在する場合、それが置き換えられることに注意してください。 それ以外の場合は追加されます。
結果を印刷するために、前のJAXPセクションのコードを再利用できます。

*5. jOOX *を使用する

https://github.com/jOOQ/jOOX[jOOX](jOOX Object-Oriented XML)は_org.w3c.dom_パッケージのラッパーであり、DOMが必要であるが冗長すぎる場合に流なXMLドキュメントの作成と操作を可能にします。 jOOXは、基礎となるドキュメントのみをラップし、DOMを強化するために使用できますが、代替として使用することはできません。

* 5.1。 Mavenの依存関係*

プロジェクトでjOOXを使用するには、_pom.xml_に依存関係を追加する必要があります。
Java 9以降で使用するには、次を使用できます。
<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>joox</artifactId>
    <version>1.6.2</version>
</dependency>
または、Java 6以降では、次のものがあります。
<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>joox-java-6</artifactId>
    <version>1.6.2</version>
</dependency>
_https://search.maven.org/search?q = g:org.jooq%20AND%20a:joox [joox] _および_https://search.maven.org/search?qの最新バージョンを見つけることができます。 Maven Centralリポジトリの= g:org.jooq%20AND%20a:joox-java-6 [joox-java-6] _

* 5.2。 org.w3c.dom.Element.setAttribute *の使用

jOOX API自体はhttps://jquery.com/[jQuery]からインスピレーションを受けています。これは以下の例で見ることができます。 使い方を見てみましょう。
まず、_Document_をロードする必要があります。
DocumentBuilder builder = JOOX.builder();
Document input = builder.parse(resourcePath);
次に、それを選択する必要があります。
Match $ = $(input);
_customer要素を選択するには、_find_メソッドまたはXPath式を使用できます。 どちらの場合も、それに一致する要素のリストを取得します。
実際の_find_メソッドを見てみましょう:
$.find("to")
    .get()
    .stream()
    .forEach(e -> e.setAttribute(attribute, newValue));
結果を_String_として取得するには、単に_toString()_メソッドを呼び出す必要があります。
$.toString();

6. 基準

これらのライブラリのパフォーマンスを比較するために、https://www.baeldung.com/java-microbenchmark-harness [JMH]ベンチマークを使用しました。
結果を見てみましょう:
| 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 |
ご覧のとおり、このユースケースと実装では、dom4jとJAXPのスコアはjOOXよりも優れています。

7. 結論

このクイックチュートリアルでは、JAXP、dom4j、jOOXを使用してXML属性を変更する方法を紹介しました。 また、JMHベンチマークでこれらのライブラリのパフォーマンスを測定しました。
通常どおり、ここに示されているすべてのコードサンプルは、https://github.com/eugenp/tutorials/tree/master/xml [GitHub]で入手できます。