Maven 坐標:org.dom4j:dom4j:2.1.4 (需額外引入 jaxen:jaxen:1.2.0 支援 XPath)。
| 介面/類 | 職責 |
|---|---|
Document | XML 文檔根節點 |
Element | 元素節點,可含屬性、子元素、文本 |
Attribute | 屬性節點 |
Node | 所有節點父介面 |
SAXReader | SAX 解析器,生成 Document |
XMLWriter | 配合 OutputFormat 輸出格式化 XML |
DocumentHelper | 工廠方法創建 Document/Element |
public void createXml(String filePath) throws IOException {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("students");
Element stu1 = root.addElement("student")
.addAttribute("id", "s001")
.addAttribute("enrolled", "2023-09-01");
stu1.addElement("name").setText("劉光藍");
stu1.addElement("gender").setText("男");
stu1.addElement("age").setText("25");
stu1.addElement("courses")
.addElement("course").addAttribute("code", "CS101").setText("資料結構")
.addElement("course").addAttribute("code", "CS102").setText("演算法");
OutputFormat fmt = OutputFormat.createPrettyPrint();
fmt.setEncoding("UTF-8");
fmt.setIndent(" ");
fmt.setNewlines(true);
try (FileWriter fw = new FileWriter(filePath);
XMLWriter writer = new XMLWriter(fw, fmt)) {
writer.write(doc);
}
}
輸出:
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="s001" enrolled="2023-09-01">
<name>劉光藍</name>
<gender>男</gender>
<age>25</age>
<courses>
<course code="CS101">資料結構</course>
<course code="CS102">演算法</course>
</courses>
</student>
</students>
public void parseXml(String filePath) throws Exception {
SAXReader reader = new SAXReader();
Document doc = reader.read(new File(filePath));
// 1. 選取所有 student 元素
List<Node> students = doc.selectNodes("//student");
for (Node node : students) {
Element stu = (Element) node;
System.out.println("ID: " + stu.attributeValue("id"));
System.out.println("Name: " + stu.elementText("name"));
System.out.println("Age: " + stu.elementText("age"));
}
// 2. 條件查詢:id 為 s001 的學生
Element target = (Element) doc.selectSingleNode("//student[@id='s001']");
if (target != null) System.out.println("Found: " + target.elementText("name"));
// 3. 遍歷課程
List<Node> courses = doc.selectNodes("//student[@id='s001']/courses/course");
for (Node n : courses) {
Element c = (Element) n;
System.out.println("Course: " + c.attributeValue("code") + " = " + c.getText());
}
}
public void updateXml(String filePath) throws Exception {
SAXReader reader = new SAXReader();
Document doc = reader.read(new File(filePath));
// 修改屬性
Element stu = (Element) doc.selectSingleNode("//student[@id='s001']");
stu.addAttribute("id", "stu001"); // 覆蓋舊屬性
// 新增子元素
stu.addElement("email").setText("liu@example.com");
// 刪除節點
Element gender = stu.element("gender");
if (gender != null) gender.detach(); // 從父節點移除
// 寫回文件
OutputFormat fmt = OutputFormat.createPrettyPrint();
fmt.setEncoding("UTF-8");
try (FileWriter fw = new FileWriter(filePath);
XMLWriter writer = new XMLWriter(fw, fmt)) {
writer.write(doc);
}
}
XML 帶命名空間時,XPath 必須註冊前綴,否則查無結果:
<root xmlns:ns="https://example.com/ns">
<ns:item id="1">Value</ns:item>
</root>
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("ns.xml"));
// 註冊命名空間上下文
Map<String, String> nsMap = Map.of("ns", "https://example.com/ns");
org.jaxen.XPath xpath = doc.createXPath("//ns:item[@id='1']");
xpath.setNamespaceContext(new SimpleNamespaceContext(nsMap));
Element item = (Element) xpath.selectSingleNode(doc);
System.out.println(item.getText()); // Value
// SimpleNamespaceContext 實現
public class SimpleNamespaceContext implements org.jaxen.NamespaceContext {
private final Map<String, String> map;
public SimpleNamespaceContext(Map<String, String> map) { this.map = map; }
@Override public String translateNamespacePrefixToUri(String prefix) { return map.get(prefix); }
@Override public String getNamespacePrefix(String uri) { return map.entrySet().stream().filter(e -> e.getValue().equals(uri)).map(Map.Entry::getKey).findFirst().orElse(null); }
@Override public Iterator<String> getPrefixes() { return map.keySet().iterator(); }
}
dom4j 提供 SAXReader + ElementHandler 邊解析邊處理,不將整樹載入記憶體:
public void streamParse(String filePath) throws Exception {
SAXReader reader = new SAXReader();
reader.addHandler("/students/student", new ElementHandler() {
@Override public void onStart(ElementPath path) {}
@Override public void onEnd(ElementPath path) {
Element stu = path.getCurrent();
// 處理單個 student
System.out.println("Processing: " + stu.attributeValue("id"));
stu.detach(); // 釋放記憶體,關鍵!
}
});
reader.read(new File(filePath)); // 觸發流式解析
}
VariableContext。detach() 已處理節點;普通解析完畢 doc = null 助 GC。reader.setFeature("https://apache.org/xml/features/disallow-doctype-decl", false)。Jackson XML / JAXB 綁定物件;高效能序列化用 Protobuf/FlatBuffers。dom4j 以 鏈式 API + 原生 XPath + 流式處理 三大殺手鐧,成為 Java XML 處理的瑞士刀。掌握「創建→解析→修改→命名空間→流式」五大場景,應對 95% 的 XML 業務需求綽綽有餘。