前言:為什麼選擇 JDOM?
Java 解析 XML 四大主流方案:DOM (樹模型、內存佔用大)、SAX (事件驅動、只讀、編碼複雜)、StAX (拉解析、流式)、JDOM (Java 原生 API、輕量、鏈式調用、支持 XPath、輸出格式化)。JDOM 2.x 專為 Java 設計,API 極其直觀,是配置文件解析、輕量級數據交換、測試數據構建的首選。
一、Maven 依賴
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.6.1</version>
</dependency>
<!-- 需額外添加 JAXP 實現 (如 Xerces) 若運行環境無內置 -->
二、核心類速覽
| 類 | 職責 |
SAXBuilder | 構建 Document (底層用 SAX 解析) |
Document | XML 文檔根節點 |
Element | 元素節點 (核心操作對象) |
Namespace | 命名空間封裝 |
Attribute | 屬性節點 |
XPathExpression | XPath 查詢 (JDOM 2.x 核心特性) |
XMLOutputter | 格式化輸出 XML |
三、實戰解析:帶命名空間的 XML 字符串
3.1 輸入 XML (含默認命名空間)
String xml = ""
+ ""
+ " "
+ " 1001 "
+ " wangwei "
+ " 80 "
+ " 1001號 "
+ "
"
+ " "
+ " 1002 "
+ " wangwei "
+ " 80 "
+ " 1002號 "
+ "
"
+ "";
3.2 完整解析代碼
import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import org.xml.sax.InputSource;
import java.io.StringReader;
import java.util.List;
public class JDomXmlParser {
public void parse(String xmlDoc) {
try {
// 1. 構建 Document
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(new InputSource(new StringReader(xmlDoc)));
// 2. 獲取根元素及其命名空間
Element root = doc.getRootElement();
Namespace ns = root.getNamespace(); // 默認命名空間: https://www.fiorano.com/...
System.out.println("根元素: " + root.getName() + ", 命名空間: " + ns.getURI());
// 3. 方式一:傳統遍歷 (需顯式傳遞 Namespace)
List<Element> rows = root.getChildren("row", ns); // 關鍵:第二個參數指定命名空間
for (Element row : rows) {
String id = row.getChildTextTrim("users_id", ns);
String name = row.getChildTextTrim("users_name", ns);
String group = row.getChildTextTrim("users_group", ns);
String address = row.getChildTextTrim("users_address", ns);
System.out.printf("ID: %s, Name: %s, Group: %s, Address: %s%n", id, name, group, address);
}
// 4. 方式二:XPath 查詢 (推薦,代碼簡潔、性能優、支持複雜路徑)
XPathFactory xpf = XPathFactory.instance();
// 編譯 XPath 表達式,註冊命名空間前綴
XPathExpression<Element> rowExpr = xpf.compile("//ns:row", Filters.element(), null,
Map.of("ns", ns.getURI()));
List<Element> xpathRows = rowExpr.evaluate(doc);
for (Element row : xpathRows) {
String id = row.getChildTextTrim("users_id", ns);
// ... 同樣需傳遞 ns
}
// 5. 方式三:無命名空間 XML 簡化處理
// 若 XML 無 xmlns,則 ns = Namespace.NO_NS,getChildren("row") 即可
} catch (JDOMException | IOException e) {
throw new RuntimeException("XML 解析失敗", e);
}
}
public static void main(String[] args) {
String xml = "..."; // 同上
new JDomXmlParser().parse(xml);
}
}
四、關鍵難點:命名空間處理詳解
- 默認命名空間 (xmlns="..."):無前綴但有 URI。查詢子元素必須顯式傳遞 Namespace 對象,否則匹配失敗 (因為無前綴元素默認屬於無命名空間)。
- 帶前綴命名空間 (xmlns:ns="..."):查詢時用
getChild("name", ns) 或 XPath 註冊前綴。
- 無命名空間:使用
Namespace.NO_NS 或直接調用無參重載。
- XPath 命名空間註冊:
Map.of("prefix", uri) 傳給 compile(),表達式中用 prefix:element。
五、常用操作速查表
| 操作 | 代碼示例 |
| 獲取元素文本 (去除首尾空白) | element.getChildTextTrim("childName", ns) |
| 獲取屬性值 | element.getAttributeValue("attrName") 或 element.getAttributeValue("attrName", ns) |
| 獲取所有子元素 | element.getChildren() / element.getChildren("name", ns) |
| 創建新元素 | new Element("name", ns) 或 new Element("name") (無 NS) |
| 添加子元素 | parent.addContent(child) |
| 設置元素文本 | element.setText("content") |
| 格式化輸出字符串 | new XMLOutputter(Format.getPrettyFormat()).outputString(doc) |
| 寫入文件 | new XMLOutputter(Format.getPrettyFormat()).output(doc, new FileWriter("out.xml")) |
六、性能與選型建議
| 場景 | 推薦方案 | 理由 |
| 配置文件解析 (小文件、需隨機訪問) | JDOM2 | API 友好、支持 XPath、內存可接受 |
| 大文件流式讀取 (GB 級) | StAX (XMLStreamReader) | 常數內存、事件驅動 |
| 高性能只讀遍歷 | SAX / StAX | 無樹構建開銷 |
| 需修改/寫入 XML | JDOM2 / DOM | 樹模型支持隨機修改 |
| 對象序列化/反序列化 | JAXB / Jackson XML / XStream | 綁定模型,業務代碼零 XML 感知 |
七、避坑指南
- 命名空間是頭號坑:默認命名空間下,
getChildren("row") 返回空列表,必須用 getChildren("row", ns)。
- 編碼一致性:XML 聲明
encoding="gb2312",Java 讀取字符串無編碼問題;讀取文件時 new InputStreamReader(is, "GBK") 顯式指定。
- 實體引用:JDOM 默認不展開外部實體 (安全),需 DTD 實體時配置
builder.setEntityResolver()。
- 線程安全:
Document/Element 非線程安全,多線程解析各自構建 SAXBuilder 實例。
八、總結
JDOM2 以純 Java 風格 API、鏈式調用、XPath 原生支持、命名空間完整處理,成為中小型 XML 處理的最佳選擇。掌握SAXBuilder 構建、Namespace 獲取傳遞、XPath 編譯查詢、XMLOutputter 格式化輸出四大核心流程,配合避坑指南,可高效應對 95% XML 解析/生成需求。