前言:為什麼選 Dom4j?
在 Java XML 處理陣營中,Dom4j 以「易用性」著稱:流暢的 API、XPath 原生支援、靈活的輸出格式、優異的效能。本文透過完整範例展示:從檔案讀取 → 解析提取 → 程式化建構 → 輸出檔案的完整閉環。
完整可執行範例程式碼
package xml;
import java.io.*;
import org.apache.log4j.Logger;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
public class Dom4jDemo {
private static final Logger logger = Logger.getLogger(Dom4jDemo.class);
public static void main(String[] args) {
// 1. 讀取 XML 檔案
File inputFile = new File("ew.xml");
SAXReader reader = new SAXReader();
try {
Document document = reader.read(inputFile);
Element root = document.getRootElement();
// 2. 提取根元素屬性
String cmd = root.attributeValue("cmd");
String version = root.attributeValue("version");
logger.debug("Root element: " + root.getName());
logger.debug("cmd=" + cmd + ", version=" + version);
// 3. 導航提取子元素
Element source = root.element("Source");
String sourceType = source.attributeValue("type");
String sourceUns = source.attributeValue("uns");
logger.debug("Source type=" + sourceType + ", uns=" + sourceUns);
Element username = root.element("Username");
String userText = username.getTextTrim();
logger.debug("Username=" + userText);
// 4. 程式化建構新 XML
Element user = DocumentHelper.createElement("User")
.addAttribute("type", "user");
user.addElement("name")
.addAttribute("type", "PinYin")
.setText("Julysea");
user.addElement("age").setText("29");
// 5. 輸出到檔案 (格式化)
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(
new FileWriter("output.xml"), format);
writer.write(user);
writer.close();
logger.info("XML 生成完成:output.xml");
} catch (DocumentException | IOException e) {
logger.error("XML 處理失敗", e);
}
}
}
關鍵技術點解析
1. 解析策略選擇
| 解析器 | 適用場景 | 記憶體特性 |
| SAXReader (DOM 風格) | 中小型 XML、需隨機存取、多次遍歷 | 完整樹載入記憶體 |
| SAXReader + XPath | 大型 XML、只需提取特定節點 | 可配合節點釋放優化 |
| StAX (XMLStreamReader) | 超大型 XML、串流處理、單向遍歷 | 事件驅動、常駐記憶體極低 |
2. XPath 強大查詢 (Dom4j 核心優勢)
// 絕對路徑
List nodes = document.selectNodes("/EW/Source/@type");
// 相對路徑 (從 root 起)
List users = root.selectNodes("//User[@type='admin']");
// 條件過濾
Element firstUser = (Element) root.selectSingleNode("User[name='Julysea']");
// 命名空間處理
Map nsMap = new HashMap();
nsMap.put("ns", "https://example.com/ns");
XPath xpath = document.createXPath("//ns:User", nsMap);
3. 輸出格式控制
// 緊湊格式 (生產環境傳輸)
OutputFormat compact = OutputFormat.createCompactFormat();
// 完美縮排 (開發除錯、配置檔)
OutputFormat pretty = OutputFormat.createPrettyPrint();
pretty.setIndent(" "); // 兩空格縮排
pretty.setEncoding("UTF-8"); // 指定編碼
pretty.setNewlines(true); // 換行
pretty.setTrimText(true); // 修剪文字節點空白
4. 命名空間正確處理
// 建立帶命名空間的元素
Namespace ns = Namespace.get("ns", "https://example.com/ns");
Element root = DocumentHelper.createElement(QName.get("Root", ns));
root.add(Namespace.get("xsi", "https://www.w3.org/2001/XMLSchema-instance"));
// 輸出時自動處理前綴宣告
Log4j 配置檔 (log4j.properties)
log4j.rootLogger=DEBUG, Console, File
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.File=org.apache.log4j.RollingFileAppender
log4j.appender.File.File=./dom4j-demo.log
log4j.appender.File.MaxFileSize=500KB
log4j.appender.File.MaxBackupIndex=1
log4j.appender.File.layout=org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
測試用 XML 範例 (ew.xml)
<?xml version="1.0" encoding="UTF-8"?>
<EW version="6.0" mod="Login" cmd="login">
<SOURCE type="user" uns="">
<USERNAME>zhangzhiyun@hp</USERNAME>
<PASSWORD>111111</PASSWORD>
<VERSION>6.01.06.00</VERSION>
</SOURCE>
</EW>
常見坑點與最佳實踐
- 編碼一致性:讀取、處理、輸出全鏈路指定 UTF-8,避免中文亂碼
- 實體引用處理:輸出含特殊字元時,`setEscapeText(true)` 確保正確轉義
- 大檔案記憶體壓力:超過 50MB XML 建議改用 StAX 或 SAX 事件驅動模式
- XPath 注入防護:使用者輸入拼接 XPath 時,必須轉義或使用參數化
- 命名空間陷阱:無命名空間查詢無法匹配有命名空間的元素,必須註冊 NamespaceContext
現代替代方案對比
| 庫 | 優點 | 缺點 | 推薦場景 |
| Dom4j | API 優雅、XPath 強、效能好 | 非標準、維護較緩 | 既有專案維護、中等複雜度 XML |
| JDOM2 | 純 Java、API 直觀 | 效能略遜 Dom4j | 教學、中小專案 |
| XOM | 極度嚴格符合 XML 標準 | 學習曲線陡、API 較死板 | 高可靠性要求系統 |
| Jackson XML | POJO 綁定、與 JSON 統一 | 複雜 XML 結構映射困難 | 資料傳輸物件序列化 |
| StAX (JDK 內建) | 零依賴、串流、記憶體極低 | 程式碼冗長、無隨機存取 | 超大檔、高吞吐管道 |