DOM 模型核心概念
- 樹狀結構:整個 XML 文檔加載為內存樹 (
Document 根節點)
- 節點類型:Element、Attribute、Text、Comment、CDATASection 等
- 優點:隨機訪問、可修改、雙向導航
- 缺點:內存佔用大 (文檔大小 5-10 倍),不適合超大文件
核心代碼:讀取 → Bean → 寫入新文件
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class XmlDomDemo {
// 1. 讀取 XML → 對象列表
public static List parseBooks(String xmlPath) {
List list = new ArrayList<>();
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new File(xmlPath));
NodeList bookNodes = doc.getElementsByTagName("book");
for (int i = 0; i < bookNodes.getLength(); i++) {
Element bookElem = (Element) bookNodes.item(i);
BookBean book = new BookBean();
book.setTitle(getTextContent(bookElem, "title"));
book.setAuthor(getTextContent(bookElem, "author"));
book.setPrice(getTextContent(bookElem, "price"));
list.add(book);
}
} catch (Exception e) {
throw new RuntimeException("XML 解析失敗: " + xmlPath, e);
}
return list;
}
// 輔助:安全獲取子元素文本內容
private static String getTextContent(Element parent, String tagName) {
NodeList nl = parent.getElementsByTagName(tagName);
if (nl.getLength() > 0) {
Node node = nl.item(0).getFirstChild();
return (node != null) ? node.getNodeValue() : "";
}
return "";
}
// 2. 對象列表 → 寫入新 XML 文件
public static void writeBooks(List books, String outputPath) {
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.newDocument();
// 創建根元素
Element root = doc.createElement("books");
doc.appendChild(root);
for (BookBean book : books) {
Element bookElem = doc.createElement("book");
root.appendChild(bookElem);
appendElement(doc, bookElem, "title", book.getTitle());
appendElement(doc, bookElem, "author", book.getAuthor());
appendElement(doc, bookElem, "price", book.getPrice());
}
// 序列化輸出 (帶縮進)
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{https://xml.apache.org/xslt}indent-amount", "2");
transformer.transform(new DOMSource(doc), new StreamResult(new File(outputPath)));
} catch (Exception e) {
throw new RuntimeException("XML 寫入失敗: " + outputPath, e);
}
}
// 輔助:創建元素並設置文本
private static void appendElement(Document doc, Element parent, String tagName, String text) {
Element elem = doc.createElement(tagName);
elem.appendChild(doc.createTextNode(text == null ? "" : text));
parent.appendChild(elem);
}
// Bean 類
public static class BookBean {
private String title, author, price;
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public String getPrice() { return price; }
public void setPrice(String price) { this.price = price; }
}
// 測試主方法
public static void main(String[] args) {
List books = parseBooks("books.xml");
System.out.println("解析到 " + books.size() + " 本書");
writeBooks(books, "new_books.xml");
System.out.println("寫入完成: new_books.xml");
}
}
關鍵 API 速查表
| 任務 | 核心類/方法 | 說明 |
| 解析文件 | DocumentBuilder.parse(File/InputStream) | 返回 Document 對象 |
| 查找元素 | getElementsByTagName(name) | 返回 NodeList,按文檔順序 |
| 獲取文本 | node.getFirstChild().getNodeValue() | Text 節點的值 |
| 創建元素 | doc.createElement(tagName) | 需 appendChild 掛載 |
| 創建文本 | doc.createTextNode(content) | 轉義特殊字符 (< > &) |
| 序列化輸出 | Transformer.transform(DOMSource, StreamResult) | 可設置 INDENT 格式化 |
| 命名空間 | doc.createElementNS(nsURI, qualifiedName) | 處理 xmlns 前綴 |
現代替代方案對比
| 方案 | 優點 | 缺點 | 適用場景 |
| DOM (標準) | 無依賴、樹操作直觀 | 內存大、代碼冗長 | 小文件、需隨機修改 |
| JAXB | 注解驅動、對象綁定自動化 | 需註解污染領域模型 | 配置文件、API 請響應 |
| Jackson XML | 高性能、流式、注解豐富 | 第三方依賴 | 高吞吐、微服務通訊 |
| XStream | 簡單、無注解、支援多態 | 安全性歷史問題 (需 1.4.18+) | 快速原型、舊系統集成 |
| SAX/StAX | 流式、低內存、高性能 | 單向只讀、邏輯複雜 | 超大文件 (GB 級) |
最佳實踐建議
- 小文件 (<10MB):DOM 代碼簡單直觀,無需引入額外依賴
- 中大文件/高性能:Jackson XML (
XmlMapper) 或 StAX (XMLStreamReader)
- 對象綁定:JAXB (Java EE 標準) 或 Jackson 注解
- 輸出格式化:
transformer.setOutputProperty(OutputKeys.INDENT, "yes") + indent-amount
- 異常處理:統一拋出
RuntimeException 携帶文件路徑與原始原因
- 編碼:輸出流指定 UTF-8,讀取時 DocumentBuilder 自動識別 XML 声明編碼
總結
DOM 解析器是 Java 標準庫處理 XML 的基石,適合小型文檔的隨機讀寫、修改、生成。核心流程:DocumentBuilder.parse → NodeList 遍歷 → getTextContent → Document 創建樹 → Transformer 序列化。現代項目中,Jackson XML 以高性能、流式處理、豐富注解成為首選;但理解 DOM 基礎 API 對於排查序列化問題、處理舊系統集成、無依賴約束環境仍具不可替代價值。