本例包含兩個完整 SAX 解析示例:一個針對特定業務數據提取(ReadXmlDemo),一個通用的全事件打印解析器。SAX 採用事件驅動、流式處理,適合大文件、低內存場景。
import java.io.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class ReadXmlDemo extends DefaultHandler {
public static void main(String[] arguments) {
if (arguments.length > 0) new ReadXmlDemo(arguments[0]);
else System.out.println("Usage: java ReadXmlDemo filename");
}
ReadXmlDemo(String xmlFile) {
File input = new File(xmlFile);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
try {
SAXParser sax = factory.newSAXParser();
sax.parse(input, new XmlHandler());
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
}
class XmlHandler extends DefaultHandler {
static final int READING_NAME = 1, READING_SEX = 2, READING_BIRTH = 3, READING_NOTHING = 0;
int currentActivity = READING_NOTHING;
ReadXml xml = new ReadXml();
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals("title")) currentActivity = READING_NAME;
else if (qName.equals("file")) currentActivity = READING_SEX;
}
public void characters(char[] ch, int start, int length) {
String value = new String(ch, start, length);
if (currentActivity == READING_NAME) xml.name = value;
if (currentActivity == READING_SEX) xml.sex = value;
}
public void endElement(String uri, String localName, String qName) {
if (qName.equals("page")) {
System.out.println("\nName: " + xml.name);
System.out.println("Sex: " + xml.sex);
xml = new ReadXml(); // 重置供下一條記錄
}
}
}
class ReadXml { String name, sex, birth; }
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;
public class SaxParsePage extends DefaultHandler {
public static void main(String[] argv) {
try {
SAXParserFactory spfactory = SAXParserFactory.newInstance();
SAXParser parser = spfactory.newSAXParser();
parser.parse(new File("book.xml"), new SaxParsePage());
} catch (Exception e) { e.printStackTrace(); }
}
public void startDocument() { System.out.println("***開始解析***"); }
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
System.out.println("節點開始:" + qName);
for (int i = 0; i < attributes.getLength(); i++) {
System.out.println("節點屬性名稱:" + attributes.getQName(i));
System.out.println("節點屬性值:" + attributes.getValue(i));
}
}
public void characters(char[] ch, int offset, int length) throws SAXException {
System.out.println("節點數據:" + new String(ch, offset, length));
}
public void endElement(String uri, String localName, String qName) {
System.out.println("節點結束:" + qName);
}
public void endDocument() { System.out.println("****文件解析完畢****"); }
}
| 方法 | 觸發時機 | 典型用途 |
|---|---|---|
startDocument() | 文檔開始 | 初始化狀態 |
startElement() | 遇到開始標籤 | 記錄元素名、屬性、切換狀態機 |
characters() | 標籤間文本內容 | 提取數據(注意可能分多次調用) |
endElement() | 遇到結束標籤 | 完成數據組裝、輸出、重置狀態 |
endDocument() | 文檔結束 | 清理資源、最終輸出 |
示例一中 currentActivity 狀態變量追蹤當前解析上下文,配合 startElement 切換狀態、characters 提取數據、endElement 完成記錄。這是 SAX 處理複雜結構的標準模式。
| 維度 | SAX | DOM |
|---|---|---|
| 內存佔用 | 極低(流式) | 高(全量樹) |
| 解析速度 | 快 | 較慢 |
| 隨機訪問 | 不支持 | 支持 |
| 編程模式 | 事件回調/狀態機 | 樹遍歷/遞歸 |
| 適用場景 | 大文件、單次遍歷、提取特定數據 | 小文件、多次查詢、需修改樹結構 |
javax.xml.parsers 無需額外依賴SAXParser 不可共享,每線程創建實例factory.setValidating(false) 提升性能InputSource.setEncoding()