Java 提供多種 XML 解析 API,各有適用場景。本文以統一範例 XML 展示四種主流方式的完整代碼與特性對比。
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
<aa><bb><cc>ccccc</cc></bb></aa>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
import javax.xml.parsers.*;
import org.w3c.dom.Document;
public class DomParserDemo {
public static void main(String[] args) throws Exception {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(DomParserDemo.class.getClassLoader().getResourceAsStream("books.xml"));
System.out.println("根節點: " + doc.getDocumentElement().getNodeName()); // bookstore
}
}
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.helpers.DefaultHandler;
public class SaxParserDemo {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(SaxParserDemo.class.getClassLoader().getResourceAsStream("books.xml"),
new DefaultHandler() {
@Override public void startElement(String uri, String local, String qName, Attributes attrs) {
System.out.println("開始元素: " + qName);
}
@Override public void characters(char[] ch, int start, int length) {
System.out.println("文本內容: " + new String(ch, start, length).trim());
}
@Override public void endElement(String uri, String local, String qName) {
System.out.println("結束元素: " + qName);
}
});
}
}
import javax.xml.stream.*;
public class StaxParserDemo {
public static void main(String[] args) throws Exception {
XMLInputFactory factory = XMLInputFactory.newFactory();
XMLStreamReader reader = factory.createXMLStreamReader(
StaxParserDemo.class.getClassLoader().getResourceAsStream("books.xml"));
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamReader.START_ELEMENT:
System.out.println("開始: " + reader.getLocalName());
break;
case XMLStreamReader.CHARACTERS:
if (reader.getText().trim().length() > 0)
System.out.println("文本: " + reader.getText());
break;
case XMLStreamReader.END_ELEMENT:
System.out.println("結束: " + reader.getLocalName());
break;
}
}
}
}
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
public class Dom4jParserDemo {
public static void main(String[] args) throws Exception {
SAXReader reader = new SAXReader();
Document doc = reader.read(Dom4jParserDemo.class.getClassLoader().getResourceAsStream("books.xml"));
// XPath 查詢展示
System.out.println("所有 cc 節點數: " + doc.selectNodes("//cc").size());
System.out.println("有 author 子節點的 book 數: " + doc.selectNodes("//book[author]").size());
System.out.println("有 category 屬性的 book 數: " + doc.selectNodes("//book[@category]").size());
System.out.println("特定條件 book/price 數: " +
doc.selectNodes("//book[author='James McGovern'][@category='WEB']/price").size());
}
}
| 維度 | DOM | SAX | StAX | DOM4J |
|---|---|---|---|---|
| 記憶體佔用 | 高 (全樹) | 極低 (事件) | 極低 (流) | 高 (全樹) |
| 解析速度 | 中 | 快 | 快 | 中 |
| 隨機存取 | 支援 | 不支援 | 不支援 | 支援 |
| 修改/寫入 | 支援 | 不支援 | 不支援 | 支援 |
| XPath 查詢 | 需額外庫 | 不支援 | 不支援 | 原生支援 |
| 學習曲線 | 低 | 中 | 中高 | 低 |
| 依賴 | JDK 內建 | JDK 內建 | JDK 內建 (Java 6+) | dom4j.jar |