DOM (Document Object Model) 將整個 XML 載入記憶體形成樹結構,適合小型文檔的隨機訪問與修改。本文演示如何使用標準 JDK API (`javax.xml.parsers`) 解析 XML 字符串,提取元素屬性與文本內容。
String xmlString = ""
+ ""
+ " yongyoulogin|用友ESB測試人員|創建|失敗|人員已存在, "
+ " "
+ " - 項目一
"
+ " - 項目二
"
+ " "
+ " ";
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.StringReader;
public class DomXmlParser {
public static void main(String[] args) throws Exception {
String xmlString = "..."; // 如上 XML
// 1. 建立 DocumentBuilderFactory (線程不安全,建議每次新建或同步使用)
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 推薦配置:防止 XXE 攻擊、命名空間感知
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.setNamespaceAware(true);
// 2. 獲取 DocumentBuilder
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 解析字符串輸入源
StringReader reader = new StringReader(xmlString);
InputSource inputSource = new InputSource(reader);
Document doc = builder.parse(inputSource);
// 4. 獲取根元素
Element root = doc.getDocumentElement(); //
System.out.println("根元素: " + root.getTagName());
// --- 方式一: 遍歷子節點 (適合結構固定) ---
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
System.out.println("元素: " + element.getTagName());
// 獲取屬性
if (element.hasAttributes()) {
NamedNodeMap attrs = element.getAttributes();
for (int j = 0; j < attrs.getLength(); j++) {
Node attr = attrs.item(j);
System.out.println(" 屬性: " + attr.getNodeName() + " = " + attr.getNodeValue());
}
}
// 獲取文本內容 (包含子元素文本)
System.out.println(" 文本: " + element.getTextContent().trim());
}
}
// --- 方式二: 精確查找 (推薦生產使用) ---
// 獲取第一個 的 value 屬性
NodeList msgList = root.getElementsByTagName("message");
if (msgList.getLength() > 0) {
Element msgEl = (Element) msgList.item(0);
String value = msgEl.getAttribute("value"); // 直接獲取屬性,不存在返回空串
String text = msgEl.getTextContent(); // 獲取標籤內文本
System.out.println("message[@value=" + value + "] text=" + text);
}
// 獲取所有 - 的 id、status 及文本
NodeList items = root.getElementsByTagName("item");
for (int i = 0; i < items.getLength(); i++) {
Element item = (Element) items.item(i);
String id = item.getAttribute("id");
String status = item.getAttribute("status");
String name = item.getTextContent().trim();
System.out.printf("Item: id=%s, status=%s, name=%s%n", id, status, name);
}
}
}
| 需求 | 方法 | 說明 |
|---|---|---|
| 獲取元素屬性值 | `element.getAttribute("name")` | 屬性不存在返回空字符串 `""`,不拋異常 |
| 獲取屬性節點對象 | `element.getAttributeNode("name")` | 返回 `Attr` 對象,可進一步操作 |
| 獲取所有屬性 | `element.getAttributes()` | 返回 `NamedNodeMap`,可迭代 |
| 獲取元素文本內容 | `element.getTextContent()` | 包含所有子孫文本節點拼接 (DOM Level 3) |
| 獲取首個子文本節點 | `element.getFirstChild().getNodeValue()` | 僅直接文本子節點,較脆弱 |
| 按標籤名查找 | `parent.getElementsByTagName("tag")` | 返回 `NodeList`,含所有後代 (非僅子代) |
| 按 ID 查找 (需 DTD/Schema 定義 ID 型) | `doc.getElementById("id")` | 需文檔聲明 ID 屬性類型,實際少用 |
XML 格式化縮進會產生 `#text` 類型節點 (換行、空格)。遍歷 `getChildNodes()` 時必須判斷 `node.getNodeType() == Node.ELEMENT_NODE`。
若 XML 含 `xmlns="..."`,需使用命名空間感知方法:
factory.setNamespaceAware(true);
// 查找
NodeList list = root.getElementsByTagNameNS("https://example.com/ns", "tagName");
解析不可信 XML 時必須關閉外部實體解析:
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.setFeature("https://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
XML 宣告 `encoding="UTF-8"` 與 Java `String` (UTF-16) 透明轉換,但若從 `InputStream` 讀取,需確保 `InputSource` 設置正確編碼:`inputSource.setEncoding("UTF-8")`。
根元素: result 元素: message 屬性: value = 0 文本: yongyoulogin|用友ESB測試人員|創建|失敗|人員已存在, 元素: data 屬性: 文本: 項目一項目二 message[@value=0] text=yongyoulogin|用友ESB測試人員|創建|失敗|人員已存在, Item: id=1001, status=active, name=項目一 Item: id=1002, status=inactive, name=項目二
DOM 解析 XML 字符串核心步驟:
配合安全配置與節點類型判斷,即可穩健提取 XML 數據。