XML 作為通用資料交換格式,在 Java 體系中擁有豐富的解析生態。本文系統對比 DOM、SAX、dom4j 三大主流方案,提供完整可執行代碼範例,幫助開發者根據場景選擇最適合的解析策略。
| 維度 | DOM (Document Object Model) | SAX (Simple API for XML) | dom4j |
|---|---|---|---|
| 原理 | 一次性載入整份文檔建立樹結構 | 事件驅動流式解析,不建立樹 | 封裝 DOM/SAX,支援 XPath、XSLT |
| 記憶體佔用 | 高(整份文檔駐留記憶體) | 極低(僅當前節點) | 中等(視使用模式) |
| 操作靈活度 | 支援增刪改查、隨機訪問 | 唯讀、單向流式 | 完整支援寫入、XPath 查詢 |
| 適用場景 | 小檔案、需多次操作、修改 XML | 大檔案、嵌入式、單次讀取 | 企業級開發、複雜 XML 處理 |
| Android 推薦度 | 低(記憶體受限) | 高(官方推薦) | 中(需引入第三方 jar) |
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version>
</dependency>
<!-- JAXP 相容 -->
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.4.01</version>
</dependency>
public class Dom4jCreateDemo {
public static void main(String[] args) throws Exception {
// 1. 建立 Document 與根元素
Document document = DocumentHelper.createDocument();
Element root = document.addElement("students");
// 2. 新增學生節點(支援屬性、巢狀結構)
Element stu1 = root.addElement("student")
.addAttribute("id", "1001")
.addAttribute("name", "張三");
stu1.addElement("course").addAttribute("name", "數學").setText("95");
stu1.addElement("course").addAttribute("name", "英文").setText("88");
Element stu2 = root.addElement("student")
.addAttribute("id", "1002")
.addAttribute("name", "李四");
stu2.addElement("course").addAttribute("name", "數學").setText("90");
stu2.addElement("course").addAttribute("name", "英文").setText("92");
// 3. 格式化輸出至檔案
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream("students.xml"), format);
writer.write(document);
writer.close();
System.out.println("XML 生成完成");
}
}
輸出結果:
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="1001" name="張三">
<course name="數學">95</course>
<course name="英文">88</course>
</student>
<student id="1002" name="李四">
<course name="數學">90</course>
<course name="英文">92</course>
</student>
</students>
public class Dom4jParseDemo {
public static void main(String[] args) throws Exception {
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("students.xml"));
Element root = doc.getRootElement();
// 1. 遍歷所有學生
for (Element student : root.elements("student")) {
String id = student.attributeValue("id");
String name = student.attributeValue("name");
System.out.printf("學生: %s (%s)%n", name, id);
// 2. XPath 查詢該學生所有課程
List courses = student.selectNodes("course");
for (Element c : courses) {
System.out.printf(" - %s: %s%n",
c.attributeValue("name"), c.getText());
}
}
// 3. 直接 XPath 定位:所有數學成績 > 90 的學生
List topMath = doc.selectNodes("//student[course[@name='數學'] > 90]");
System.out.println("\n數學優秀學生:");
for (Element s : topMath) {
System.out.println(" " + s.attributeValue("name"));
}
}
}
適合超大檔案、記憶體極度受限環境(如 Android)。核心流程:
SAXParserFactory → SAXParserDefaultHandler 覆寫四大回調方法parse(InputStream, handler)public class SaxParserDemo extends DefaultHandler {
private List persons = new ArrayList<>();
private Person currentPerson;
private String currentTag;
@Override
public void startDocument() {
persons = new ArrayList<>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) {
currentTag = localName;
if ("person".equals(localName)) {
currentPerson = new Person();
currentPerson.setId(attrs.getValue("id"));
}
}
@Override
public void characters(char[] ch, int start, int length) {
if (currentPerson == null) return;
String value = new String(ch, start, length).trim();
if (value.isEmpty()) return;
switch (currentTag) {
case "name": currentPerson.setName(value); break;
case "age": currentPerson.setAge(value); break;
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if ("person".equals(localName) && currentPerson != null) {
persons.add(currentPerson);
currentPerson = null;
}
currentTag = "";
}
public List parse(InputStream is) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(is, this);
return persons;
}
// 測試主程式
public static void main(String[] args) throws Exception {
try (InputStream is = new FileInputStream("persons.xml")) {
List list = new SaxParserDemo().parse(is);
list.forEach(p -> System.out.println(p.getId() + " " + p.getName() + " " + p.getAge()));
}
}
}
public class DomParserDemo {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("students.xml"));
// 1. 讀取
NodeList students = doc.getElementsByTagName("student");
for (int i = 0; i < students.getLength(); i++) {
Element stu = (Element) students.item(i);
System.out.println(stu.getAttribute("name"));
}
// 2. 修改:將第一位學生姓名改為 "王五"
Element first = (Element) students.item(0);
first.setAttribute("name", "王五");
// 3. 新增節點
Element newStu = doc.createElement("student");
newStu.setAttribute("id", "1003");
newStu.setAttribute("name", "趙六");
doc.getDocumentElement().appendChild(newStu);
// 4. 寫回檔案
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(new DOMSource(doc), new StreamResult(new File("students_modified.xml")));
}
}
需要修改/寫入 XML?
├─ 是 → 檔案 < 50MB? → dom4j / DOM
└─ 否 → 僅讀取?
├─ 檔案 > 100MB / Android / 記憶體受限 → SAX
├─ 需 XPath 複雜查詢 → dom4j
└─ 簡單遍歷、標準 API 偏好 → DOM / SAX
實務上常組合使用:SAX 快速篩選大檔案關鍵片段 → dom4j 精細處理片段。