SAX (Simple API for XML) 是事件驅動的串流式解析器,不將整份 XML 載入記憶體,適合處理大型 XML 檔案。本文提供完整範例,示範如何實作自訂 DefaultHandler 解析包含屬性與巢狀元素的 XML,並映射為 Java 物件清單。
<People id="1" en="John">
<Name>張三</Name>
<Age>25</Age>
</People>
<People id="2" en="Jane">
<Name>李四</Name>
<Age>30</Age>
</People>
public class People {
private String id, name, englishName, age;
// Getter/Setter 略
}
class SaxHandler extends DefaultHandler {
private List<People> peoples = new ArrayList<>();
private People currentPeople;
private String currentTag;
private final String nodeName; // 目標根元素名稱
public SaxHandler(String nodeName) {
this.nodeName = nodeName;
}
public List<People> getPeoples() { return peoples; }
@Override
public void startDocument() {
peoples = new ArrayList<>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) {
currentTag = qName;
if (qName.equals(nodeName)) {
currentPeople = new People();
}
if (attrs != null && currentPeople != null) {
for (int i = 0; i < attrs.getLength(); i++) {
String attrName = attrs.getQName(i);
String attrVal = attrs.getValue(i);
if ("id".equals(attrName)) currentPeople.setId(attrVal);
else if ("en".equals(attrName)) currentPeople.setEnglishName(attrVal);
}
}
}
@Override
public void characters(char[] ch, int start, int length) {
if (currentTag != null && currentPeople != null) {
String value = new String(ch, start, length).trim();
if (!value.isEmpty() && !value.equals("\n")) {
switch (currentTag) {
case "Name": currentPeople.setName(value); break;
case "Age": currentPeople.setAge(value); break;
}
}
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if (qName.equals(nodeName) && currentPeople != null) {
peoples.add(currentPeople);
currentPeople = null;
}
}
}
public class SaxXML {
public static void main(String[] args) {
File file = new File("e:/People.xml");
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
SaxHandler handler = new SaxHandler("People");
parser.parse(new FileInputStream(file), handler);
for (People p : handler.getPeoples()) {
System.out.println(p.getId() + "\t" + p.getName() + "\t" + p.getEnglishName() + "\t" + p.getAge());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
| 特性 | 說明 |
|---|---|
| 事件驅動 | 依序觸發 startElement、characters、endElement,需自行管理狀態 |
| 低記憶體 | 不建立 DOM 樹,適合超大 XML(百 MB 以上) |
| 單向讀取 | 無法隨機存取或回溯,不支援 XPath |
| characters 可能分段 | 長文字內容可能多次觸發,需自行累加(本範例簡化處理) |
| 空白文字節點 | 格式化 XML 的縮排換行也會觸發 characters,需 trim() 過濾 |
factory.setValidating(false)(預設關閉)factory.setNamespaceAware(false)(若不需命名空間)SAXParser 實例解析多份結構相同的 XML此範例展示了 SAX 解析的標準模式,可作為處理大型 XML 資料交換(如報表匯入、日誌分析)的基礎模板。