1. 简介
在处理 XML 文件时,我们经常会操作其属性(attribute)。本文将介绍如何使用 Java 修改 XML 文件中的属性值。我们将使用三种常见方式:JAXP、dom4j 和 jOOX,并对比它们的性能。
2. 依赖配置
为了运行测试用例,我们需要在 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>
✅ 实现步骤
- 使用
DocumentBuilderFactory
加载 XML 文档 - 配置安全特性防止 XXE 攻击
- 使用 XPath 查找目标节点
- 修改属性值
- 使用
Transformer
输出修改后的 XML
🧩 示例代码
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);
XPath xpath = XPathFactory
.newInstance()
.newXPath();
String expr = String.format("//*[contains(@%s, '%s')]", attribute, oldValue);
NodeList nodes = (NodeList) xpath.evaluate(expr, input, XPathConstants.NODESET);
IntStream
.range(0, nodes.getLength())
.mapToObj(i -> (Element) nodes.item(i))
.forEach(value -> value.setAttribute(attribute, newValue));
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));
输出结果:
<?xml version="1.0" encoding="UTF-8"?>
<notification id="5">
<to customer="false">[email protected]</to>
<from>[email protected]</from>
</notification>
✅ 单元测试验证
assertThat(output.toString()).hasXPath("//*[contains(@customer, 'false')]");
4. 使用 dom4j 修改属性
dom4j 是一个功能强大的开源 XML 处理库,支持 XPath、DOM、SAX 等多种解析方式。
4.1 Maven 依赖
<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>
4.2 修改属性
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);
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);
}
⚠️ 注意:addAttribute
方法会自动替换已有属性值。
5. 使用 jOOX 修改属性
jOOX 是对 org.w3c.dom
的封装,提供类似 jQuery 的链式调用语法,适合需要 DOM 支持但又不想写冗长代码的场景。
5.1 Maven 依赖(Java 6+)
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joox-java-6</artifactId>
<version>1.6.2</version>
</dependency>
5.2 修改属性示例
DocumentBuilder builder = JOOX.builder();
Document input = builder.parse(resourcePath);
Match $ = $(input);
$.find("to")
.get()
.stream()
.forEach(e -> e.setAttribute(attribute, newValue));
输出结果:
$.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 性能略逊,但 API 更加简洁易用
7. 小结
本文介绍了三种使用 Java 修改 XML 属性的方式:
方式 | 特点 | 适用场景 |
---|---|---|
JAXP | Java 原生支持 | 不想引入第三方库时使用 |
dom4j | 性能好,功能丰富 | 需要灵活处理 XML 的场景 |
jOOX | 语法简洁,类似 jQuery | 快速开发,追求可读性 |
✅ 推荐顺序:dom4j > JAXP > jOOX
完整代码示例可在 GitHub 仓库 中找到。