JavaでのXML属性の変更
1. 序章
XMLを使用する場合の一般的なアクティビティの1つは、その属性を使用することです。 このチュートリアルでは、Javaを使用してXML属性を変更する方法について説明します。
2. 依存関係
テストを実行するには、JUnitおよびxmlunit-assertj依存関係をMavenプロジェクトに追加する必要があります。
<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. 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 クラスの外部エンティティ処理(XXE)を無効にするために、XMLConstants.FEATURE_SECURE_PROCESSINGおよびhttp://apache.org/xml/featuresを構成することに注意してください。 / disallow-doctype-declfeatures。 信頼できないXMLファイルを解析するときに構成することをお勧めします。
input オブジェクトを初期化した後、変更する属性を持つノードを見つける必要があります。 XPath式を使用して選択してみましょう。
XPath xpath = XPathFactory
.newInstance()
.newXPath();
String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue);
NodeList nodes = (NodeList) xpath.evaluate(expr, input, XPathConstants.NODESET);
この場合、XPath Evaluation メソッドは、一致したノードを含むノードリストを返します。
リストを繰り返し処理して、値を変更してみましょう。
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>
また、単体テストで検証する必要がある場合は、XMLUnitのassertThatメソッドを使用できます。
assertThat(output.toString()).hasXPath("//*[contains(@customer, 'false')]");
4. dom4jの使用
dom4j は、XMLを処理するためのオープンソースフレームワークであり、XPathと統合されており、DOM、SAX、JAXP、およびJavaコレクションを完全にサポートしています。
4.1. Mavenの依存関係
プロジェクトでdom4jを使用するには、dom4jおよびjaxenの依存関係をpom.xmlに追加する必要があります。
<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>
dom4jの詳細については、XMLライブラリサポートの記事を参照してください。
4.2. org.dom4j.Element.addAttributeを使用する
dom4jは、XML要素の抽象化としてElementインターフェースを提供します。 addAttribute メソッドを使用して、customer属性を更新します。
これがどのように機能するか見てみましょう。
まず、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を使用する
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>
jooxおよびjoox-java-6の最新バージョンはMavenCentralリポジトリーにあります。
5.2. org.w3c.dom.Element.setAttributeを使用する
以下の例でわかるように、jOOXAPI自体はjQueryに触発されています。 使い方を見てみましょう。
まず、ドキュメントをロードする必要があります。
DocumentBuilder builder = JOOX.builder();
Document input = builder.parse(resourcePath);
次に、それを選択する必要があります。
Match $ = $(input);
customer Element、を選択するには、findメソッドまたはXPath式を使用できます。 どちらの場合も、それに一致する要素のリストを取得します。
findメソッドの動作を見てみましょう。
$.find("to")
.get()
.stream()
.forEach(e -> e.setAttribute(attribute, newValue));
結果をStringとして取得するには、 toString()メソッドを呼び出すだけです。
$.toString();
6. 基準
これらのライブラリのパフォーマンスを比較するために、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ベンチマークを使用してこれらのライブラリのパフォーマンスを測定しました。
いつものように、ここに示されているすべてのコードサンプルは、GitHubから入手できます。