XML(可扩展标记语言)凭借其结构清晰、跨平台兼容的特性,在配置文件存储、数据交换等场景中应用广泛。在实际开发中,我们经常会遇到需要修改XML文件内容的需求,比如新增节点、删除冗余配置、更新节点属性等。不同的XML解析方式实现增删改的逻辑存在差异,选择合适的解析工具能让开发效率大幅提升。

XML解析方式对比
常见的XML解析方式主要分为DOM解析、SAX解析、JDOM解析和DOM4J解析四种,不同方式的特性适合不同的操作场景:
| 解析方式 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| DOM解析 | 将整个XML文档加载到内存,形成树形结构 | 支持随机访问,可方便实现增删改操作 | 文档过大时内存占用高 | 小到中型XML文件的修改操作 |
| SAX解析 | 基于事件驱动,边读边解析,不加载整个文档 | 内存占用低,解析速度快 | 只能顺序读取,不支持修改操作 | 大型XML文件的只读解析 |
| JDOM解析 | 基于DOM改进,使用Java集合类简化操作 | API更符合Java开发习惯 | 性能略低于DOM4J | Java项目中简单的XML操作 |
| DOM4J解析 | 基于JDOM优化,性能和灵活性更优 | 支持XPath查询,API丰富,操作便捷 | 需要额外导入依赖包 | 复杂XML增删改场景,推荐使用 |
如果需要对XML进行增删改操作,DOM和DOM4J是优先选择,其中DOM4J的API更简洁,还支持XPath快速定位节点,实际开发中应用最多。本文后续示例都将基于DOM4J展开。
环境准备
使用DOM4J需要先导入对应的依赖包,如果是Maven项目,在pom.xml中添加如下依赖:
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version>
</dependency>
<!-- 如果需要使用XPath,还需要添加jaxen依赖 -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>如果是普通Java项目,可以手动下载dom4j和jaxen的jar包,导入到项目的类路径中即可。
XML增删改操作实现
我们先准备一个测试用的XML文件,命名为test.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>张三</name>
<age>25</age>
<email>zhangsan@ipipp.com</email>
</user>
<user id="2">
<name>李四</name>
<age>28</age>
<email>lisi@ipipp.com</email>
</user>
</users>1. 新增XML节点
新增节点分为两种情况:在指定父节点下新增子节点,以及在指定位置插入节点。下面演示在users根节点下新增一个user子节点:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class XmlAddDemo {
public static void main(String[] args) throws DocumentException, IOException {
// 1. 创建SAXReader读取XML文件
SAXReader reader = new SAXReader();
Document document = reader.read(new File("test.xml"));
// 2. 获取根节点
Element rootElement = document.getRootElement();
// 3. 新增user节点,并设置属性
Element newUser = rootElement.addElement("user");
newUser.addAttribute("id", "3");
// 4. 新增user下的子节点
Element name = newUser.addElement("name");
name.setText("王五");
Element age = newUser.addElement("age");
age.setText("30");
Element email = newUser.addElement("email");
email.setText("wangwu@ipipp.com");
// 5. 设置输出格式,避免中文乱码,格式化输出
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
// 6. 写入文件
XMLWriter writer = new XMLWriter(new FileOutputStream("test.xml"), format);
writer.write(document);
writer.close();
System.out.println("新增节点成功");
}
}运行上述代码后,test.xml中会增加一个id为3的用户节点。如果需要在指定位置插入节点,可以先获取父节点的所有子节点列表,再调用add(int index, Element element)方法插入,示例代码如下:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class XmlInsertDemo {
public static void main(String[] args) throws DocumentException, IOException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("test.xml"));
Element rootElement = document.getRootElement();
// 获取所有user子节点
List<Element> userList = rootElement.elements("user");
// 创建要插入的新节点
Element insertUser = rootElement.createElement("user");
insertUser.addAttribute("id", "1.5");
Element name = insertUser.addElement("name");
name.setText("赵六");
Element age = insertUser.addElement("age");
age.setText("26");
Element email = insertUser.addElement("email");
email.setText("zhaoliu@ipipp.com");
// 在第二个位置(索引1)插入新节点
userList.add(1, insertUser);
// 重新整理根节点的子节点顺序
rootElement.clearContent();
for (Element user : userList) {
rootElement.add(user);
}
// 写入文件
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream("test.xml"), format);
writer.write(document);
writer.close();
System.out.println("插入节点到指定位置成功");
}
}2. 修改XML节点
修改XML节点通常需要先定位到目标节点,再修改其属性或文本内容。结合XPath可以快速定位节点,比如修改id为2的用户的年龄:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.xpath.DefaultXPath;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class XmlUpdateDemo {
public static void main(String[] args) throws DocumentException, IOException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("test.xml"));
// 使用XPath定位id为2的user节点
DefaultXPath xPath = new DefaultXPath("//user[@id='2']");
List<Element> nodes = xPath.selectNodes(document);
if (nodes != null && !nodes.isEmpty()) {
Element targetUser = nodes.get(0);
// 修改age子节点的文本
Element ageElement = targetUser.element("age");
if (ageElement != null) {
ageElement.setText("29");
}
// 修改user节点的属性
targetUser.addAttribute("id", "2"); // 如果要修改属性值,直接设置即可,会覆盖原有值
targetUser.addAttribute("status", "active"); // 新增属性
}
// 写入文件
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream("test.xml"), format);
writer.write(document);
writer.close();
System.out.println("修改节点成功");
}
}如果没有使用XPath,也可以通过遍历所有节点来定位目标节点,适合结构简单的XML文件:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
public class XmlUpdateDemo2 {
public static void main(String[] args) throws DocumentException, IOException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("test.xml"));
Element rootElement = document.getRootElement();
// 遍历所有user节点
Iterator<Element> userIterator = rootElement.elementIterator("user");
while (userIterator.hasNext()) {
Element user = userIterator.next();
String id = user.attributeValue("id");
if ("2".equals(id)) {
// 找到目标节点,修改内容
Element nameElement = user.element("name");
if (nameElement != null) {
nameElement.setText("李四更新");
}
break;
}
}
// 写入文件
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream("test.xml"), format);
writer.write(document);
writer.close();
System.out.println("遍历修改节点成功");
}
}3. 删除XML节点
删除节点同样需要先定位到目标节点,再调用父节点的remove(Element element)方法删除。下面演示删除id为1.5的用户节点:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.xpath.DefaultXPath;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class XmlDeleteDemo {
public static void main(String[] args) throws DocumentException, IOException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("test.xml"));
// 使用XPath定位要删除的节点
DefaultXPath xPath = new DefaultXPath("//user[@id='1.5']");
List<Element> nodes = xPath.selectNodes(document);
if (nodes != null && !nodes.isEmpty()) {
Element targetUser = nodes.get(0);
// 获取父节点,调用remove方法删除
Element parent = targetUser.getParent();
if (parent != null) {
parent.remove(targetUser);
}
}
// 写入文件
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream("test.xml"), format);
writer.write(document);
writer.close();
System.out.println("删除节点成功");
}
}如果要删除节点的某个属性或者子节点,也可以直接调用对应节点的remove方法:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class XmlDeleteAttrDemo {
public static void main(String[] args) throws DocumentException, IOException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("test.xml"));
Element rootElement = document.getRootElement();
// 遍历user节点,删除status属性
Iterator<Element> userIterator = rootElement.elementIterator("user");
while (userIterator.hasNext()) {
Element user = userIterator.next();
// 删除status属性
user.remove(user.attribute("status"));
// 删除age子节点
Element age = user.element("age");
if (age != null) {
user.remove(age);
}
}
// 写入文件
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream("test.xml"), format);
writer.write(document);
writer.close();
System.out.println("删除属性和子节点成功");
}
}注意事项
- 操作XML文件前建议先备份原文件,避免修改出错导致数据丢失。
- 写入文件时要设置正确的编码格式,否则容易出现中文乱码问题,通常XML文件使用UTF-8编码。
- 使用DOM4J操作大文件时,如果文件超过内存限制,建议分块处理,或者改用SAX解析后生成新的XML文件。
- XPath表达式要注意语法的正确性,如果定位不到节点,可以先打印所有节点信息排查问题。
- 修改属性时,如果属性已存在,直接调用
addAttribute会覆盖原有值,不需要先删除再添加。
总结
XML的增删改操作核心是先通过解析工具将XML加载为内存中的文档对象,再通过API操作对应的节点,最后将修改后的文档写回文件。DOM4J作为目前最常用的XML解析工具,兼顾了性能和易用性,配合XPath可以快速定位节点,大幅提升开发效率。实际开发中可以根据XML文件的大小和操作复杂度,选择合适的解析方式和实现逻辑,同时注意编码和备份等细节问题,就能顺利完成XML内容的修改需求。