DOM (Document Object Model) 將整個 XML 文檔載入內存形成樹狀結構,支援隨機訪問、修改、寫回,適合中小型文檔(< 50MB)的全量處理。本文以 JDK 1.3/1.4 + Apache Crimson 為環境,示範標準 JAXP API 讀寫「學生花名冊」XML,代碼兼容性強,易於遷移至 Xerces、JAXP 1.4+ 等新版本。
Input.xml)<?xml version="1.0" encoding="GB2312"?>
<學生花名冊>
<學生 性別="男">
<姓名>李華</姓名>
<年齡>14</年齡>
<電話>6287555</電話>
</學生>
<學生 性別="男">
<姓名>張三</姓名>
<年齡>16</年齡>
<電話>8273425</電話>
</學生>
</學生花名冊>
public class StudentBean {
private String sex, name, phone;
private int age;
// 標準 getter/setter 省略...
}
import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.apache.crimson.tree.XmlDocument; // 僅寫入時需強轉
private void readXMLFile(String inFile) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File(inFile));
Element root = doc.getDocumentElement(); // 學生花名冊
NodeList students = root.getElementsByTagName("學生");
for (int i = 0; i < students.getLength(); i++) {
Element stuElem = (Element) students.item(i);
StudentBean bean = new StudentBean();
bean.setSex(stuElem.getAttribute("性別"));
bean.setName(getSingleChildText(stuElem, "姓名"));
bean.setAge(Integer.parseInt(getSingleChildText(stuElem, "年齡")));
bean.setPhone(getSingleChildText(stuElem, "電話"));
studentList.add(bean);
}
}
// 輔助:取單一子元素文本
private String getSingleChildText(Element parent, String tagName) {
NodeList nl = parent.getElementsByTagName(tagName);
if (nl.getLength() == 1) {
return ((Text) nl.item(0).getFirstChild()).getNodeValue();
}
return "";
}
private void writeXMLFile(String outFile) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
Element root = doc.createElement("學生花名冊");
doc.appendChild(root);
for (StudentBean bean : studentList) {
Element stu = doc.createElement("學生");
stu.setAttribute("性別", bean.getSex());
root.appendChild(stu);
appendTextElement(doc, stu, "姓名", bean.getName());
appendTextElement(doc, stu, "年齡", String.valueOf(bean.getAge()));
appendTextElement(doc, stu, "電話", bean.getPhone());
}
// 關鍵:強轉為 Crimson 專有類以調用 write()
try (FileOutputStream fos = new FileOutputStream(outFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, "GB2312")) {
((XmlDocument) doc).write(osw, "GB2312"); // 解決中文亂碼
}
}
private void appendTextElement(Document doc, Element parent, String tag, String text) {
Element e = doc.createElement(tag);
e.appendChild(doc.createTextNode(text));
parent.appendChild(e);
}
public static void main(String[] args) throws Exception {
XMLTest test = new XMLTest();
test.studentList = new ArrayList<>();
test.readXMLFile("Input.xml");
test.writeXMLFile("Output.xml");
System.out.println("完成!對比 Input.xml 與 Output.xml 應完全一致。");
}
| 環節 | 要點 |
|---|---|
| 解析器選擇 | JAXP DocumentBuilderFactory 可換裝 Xerces/Crimson,無需改代碼 |
| 中文編碼 | 讀取自動檢測;寫入必須指定 OutputStreamWriter(..., "GB2312") 並強轉 XmlDocument 調用 write(Writer, encoding) |
| 異常處理 | 捕獲 ParserConfigurationException、SAXException、IOException,避免資源洩漏 |
| 內存考量 | 大文件請改用 SAX/StAX 流式處理,避免 OOM |
org.apache.crimson.tree.XmlDocument 依賴,改用標準 javax.xml.transform (Transformer) 輸出,支援任意編碼與縮排。DocumentBuilderFactory.setNamespaceAware(true) 處理命名空間。StAX (XMLStreamReader/Writer) 或 Woodstox。DOM 模式代碼直觀、易調試,是 Java XML 入門的最佳範本。掌握「解析→樹遍歷→物件映射→樹構建→序列化」完整鏈路,即可應對絕大多數中小型 XML 業務場景。