DOM (Document Object Model) 將整份 XML 載入記憶體構建樹狀結構,支援隨機存取、XPath 查詢、修改寫入。適合中小型 XML、需多次遍歷/修改的場景。本文提供完整解析範例,示範元素屬性、巢狀子元素、文字內容的提取與物件映射。
<?xml version="1.0" encoding="UTF-8"?>
<PeopleList>
<People id="1" en="John">
<Name>張三</Name>
<Age>25</Age>
</People>
<People id="2" en="Jane">
<Name>李四</Name>
<Age>30</Age>
</People>
</PeopleList>
public class People {
private String id, name, englishName, age;
// Getter/Setter/建構子/toString 略
}
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class DomXML {
public static void main(String[] args) {
File file = new File("e:/People.xml");
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// factory.setNamespaceAware(true); // 若 XML 有命名空間
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(file);
doc.getDocumentElement().normalize(); // 合併相鄰文字節點
List peopleList = new ArrayList<>();
NodeList peopleNodes = doc.getElementsByTagName("People");
for (int i = 0; i < peopleNodes.getLength(); i++) {
Node node = peopleNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element peopleElem = (Element) node;
People people = new People();
// 1. 讀取屬性
people.setId(peopleElem.getAttribute("id"));
people.setEnglishName(peopleElem.getAttribute("en"));
// 2. 讀取子元素文字內容
NodeList children = peopleElem.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node child = children.item(j);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element childElem = (Element) child;
String tagName = childElem.getTagName();
String textContent = childElem.getTextContent().trim();
switch (tagName) {
case "Name": people.setName(textContent); break;
case "Age": people.setAge(textContent); break;
}
}
}
peopleList.add(people);
}
}
// 輸出驗證
for (People p : peopleList) {
System.out.println(p.getId() + "\t" + p.getName() + "\t" + p.getEnglishName() + "\t" + p.getAge());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
| 介面/方法 | 用途 | 備註 |
|---|---|---|
DocumentBuilderFactory | 建立解析器工廠 | 可設定驗證、命名空間、忽略註解等 |
DocumentBuilder.parse() | 解析輸入源 → Document | 支援 File, InputStream, InputSource, URI |
doc.getDocumentElement() | 取得根元素 | 對應 XML 根標籤 |
getElementsByTagName() | 遞迴搜尋所有同名元素 | 回傳 NodeList (即時檢視) |
Element.getAttribute() | 讀取屬性值 | 屬性不存在回傳空字串 |
Node.getTextContent() | 取得元素及所有後代文字 | 包含換行縮排,需 trim() |
Node.getNodeType() | 判斷節點類型 | ELEMENT_NODE=1, TEXT_NODE=3, 等 |
doc.normalize() | 合併相鄰文字節點 | 解決格式化 XML 產生多文字節點問題 |
| 特性 | DOM | SAX | StAX |
|---|---|---|---|
| 記憶體模型 | 完整樹狀結構 (高) | 串流事件 (極低) | 游標/迭代器 (低) |
| 存取模式 | 隨機、雙向 | 單向、唯讀 | 單向、可拉取 |
| 修改/寫入 | 原生支援 | 不支援 | 支援 (XMLStreamWriter) |
| XPath 支援 | 原生 | 需自行實作 | 需額外庫 |
| 適用場景 | 小檔、多遍歷、需修改 | 大檔、單遍歷、提取部分 | 大檔、需控制流程、雙向 |
getNodeType() == ELEMENT_NODExmlns,需啟用 factory.setNamespaceAware(true) 並使用 getElementsByTagNameNS()encoding 與實際檔案編碼必須一致,解析器自動處理factory.setFeature("https://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("https://xml.org/sax/features/external-general-entities", false);
factory.setFeature("https://xml.org/sax/features/external-parameter-entities", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
import javax.xml.xpath.*;
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
NodeList nodes = (NodeList) xpath.evaluate("//People", doc, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
Element e = (Element) nodes.item(i);
String id = e.getAttribute("id");
String name = xpath.evaluate("Name/text()", e);
String age = xpath.evaluate("Age/text()", e);
// ...
}
DOM 解析以直觀樹狀模型和豐富 API 成為中小型 XML 處理的首選,配合 XPath 更能大幅簡化提取邏輯。