DOM 解析器建立與載入
public static Document getDocument(String filename) throws Exception {
// 1. 建立工廠
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 取得 DOM 解析器
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 解析 XML 檔案,回傳 Document 物件樹
return builder.parse(filename);
}
範例 XML 結構 (books.xml)
<書架>
<書>
<書名 name="xxx">Java就業培訓教程書名>
<作者>張孝祥作者>
<售價>59.00元售價>
書>
<書>
<書名>JavaScript網頁開發書名>
<作者>張孝祥作者>
<售價>28.00元售價>
書>
書架>
核心操作 API
1. 讀取單一節點
NodeList list = document.getElementsByTagName("書名");
Node node = list.item(1); // 索引 1 = 第二本書
String content = node.getTextContent();
System.out.println(content); // 輸出:JavaScript網頁開發
2. 遞迴遍歷所有節點
Node root = document.getElementsByTagName("書架").item(0);
printNode(root, 0);
void printNode(Node node, int depth) {
if (node instanceof Element) {
System.out.println(" ".repeat(depth) + node.getNodeName());
}
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
printNode(children.item(i), depth + 1);
}
}
3. 取得屬性值
Element bookName = (Element) document.getElementsByTagName("書名").item(0);
String attrValue = bookName.getAttribute("name"); // "xxx"
System.out.println(attrValue);
4. 新增節點 (附加到第一本書)
// 建立新元素
Element price = document.createElement("售價");
price.setTextContent("59.00元");
// 掛載到目標父節點
Element book = (Element) document.getElementsByTagName("書").item(0);
book.appendChild(price);
// 寫回檔案
writeBack(document, filename);
5. 指定位置插入 (在第一個 <售價> 前插入)
Element price = document.createElement("售價");
price.setTextContent("49.00元");
Element refNode = (Element) document.getElementsByTagName("售價").item(0);
Element book = (Element) document.getElementsByTagName("書").item(0);
book.insertBefore(price, refNode); // 插入到 refNode 之前
writeBack(document, filename);
6. 設定/修改屬性
Element bookName = (Element) document.getElementsByTagName("書名").item(0);
bookName.setAttribute("name", "xxxxxx"); // 新增或更新屬性
writeBack(document, filename);
7. 刪除節點
Element priceNode = (Element) document.getElementsByTagName("售價").item(0);
priceNode.getParentNode().removeChild(priceNode);
writeBack(document, filename);
通用寫回工具方法
private static void writeBack(Document doc, String filename) throws Exception {
TransformerFactory tfFactory = TransformerFactory.newInstance();
Transformer transformer = tfFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 格式化縮排
transformer.setOutputProperty("{https://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(
new DOMSource(doc),
new StreamResult(new FileOutputStream(filename))
);
}
DOM vs SAX vs StAX 選擇指南
| 解析器 | 模式 | 記憶體 | 速度 | 適用場景 |
| DOM | 樹狀模型 (整份載入記憶體) | 高 | 中 | 需隨機存取、修改結構、文件較小 (<50MB) |
| SAX | 事件驅動 (串流單向) | 極低 | 快 | 唯讀、大檔案、記憶體受限、只需特定資料 |
| StAX | 拉取式 (游標控制) | 低 | 快 | 需控制解析流程、雙向操作、大檔案處理 |
常見坑點提醒
getChildNodes() 包含文字節點 (換行、空白),遍歷時需檢查 node.getNodeType() == Node.ELEMENT_NODE
getTextContent() 會遞迴串聯所有子文字節點,getNodeValue() 僅取得當前節點值
- 命名空間 XML 需啟用
factory.setNamespaceAware(true) 並使用 getElementsByTagNameNS
- 大檔案請改用 SAX/StAX,DOM 會造成 OOM