在系統整合、配置遷移、異構數據交換等場景中,將 XML 文檔轉換為 JSON 格式是常見需求。相較於 DOM 解析需將整棵樹載入記憶體,SAX (Simple API for XML) 採用事件驅動的流式解析,記憶體佔用極低,適合處理大型 XML 文件。本文提供一套完整、可生產環境使用的 XMLToJSON 工具類,核心包含三個類:事件處理器 ToJsonSAXHandler、異常類 XMLToJSONException、門面類 XMLToJSON。
DefaultHandler,處理 startElement、endElement、characters 等 SAX 事件,維護一個 StringBuilder 構建 JSON 字串。關鍵狀態標記:isProcessing (是否正在解析)、justProcessStartElement (判斷是否需輸出逗號分隔)。convert(String)、convert(File)、convert(InputSource) 三種重載,內部創建 XMLReader 並註冊 Handler。public class ToJsonSAXHandler extends DefaultHandler {
private StringBuilder jsonStringBuilder = new StringBuilder();
private boolean isProcessing = false;
private boolean justProcessStartElement = true;
@Override
public void startDocument() {
isProcessing = true;
justProcessStartElement = true;
jsonStringBuilder.setLength(0);
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) {
if (!justProcessStartElement) {
jsonStringBuilder.append(',');
}
justProcessStartElement = true;
jsonStringBuilder.append("{")
.append("\"localName\":\"").append(localName).append("\",")
.append("\"uri\":\"").append(uri).append("\",")
.append("\"qName\":\"").append(qName).append("\",")
.append("\"attrs\":{");
// 處理屬性
if (attrs.getLength() > 0) {
jsonStringBuilder.append("\"").append(attrs.getLocalName(0)).append("\":\"")
.append(attrs.getValue(0)).append("\"");
for (int i = 1; i < attrs.getLength(); i++) {
jsonStringBuilder.append(",\"").append(attrs.getLocalName(i)).append("\":\"")
.append(attrs.getValue(i)).append("\"");
}
}
jsonStringBuilder.append("},\"childElements\":[\n");
}
@Override
public void endElement(String uri, String localName, String qName) {
justProcessStartElement = false;
jsonStringBuilder.append("]}\n");
}
@Override
public void characters(char[] ch, int start, int length) {
if (!justProcessStartElement) {
jsonStringBuilder.append(',');
} else {
justProcessStartElement = false;
}
jsonStringBuilder.append('"');
for (int i = start; i < start + length; i++) {
char c = ch[i];
switch (c) {
case '\'': jsonStringBuilder.append("\\'"); break;
case '"': jsonStringBuilder.append("\\\""); break;
case '\n': jsonStringBuilder.append("\\n"); break;
case '\t': jsonStringBuilder.append("\\t"); break;
case '\r': jsonStringBuilder.append("\\r"); break;
default: jsonStringBuilder.append(c);
}
}
jsonStringBuilder.append("\"\n");
}
public String getJsonString() {
if (isProcessing) throw new XMLToJSONException("Parsing not finished");
return jsonStringBuilder.toString();
}
}
public class XMLToJSON {
public static String convert(String xmlStr) throws SAXException, IOException, XMLToJSONException {
return convert(new InputSource(new StringReader(xmlStr)));
}
public static String convert(File file) throws SAXException, IOException, XMLToJSONException {
return convert(new InputSource(new FileInputStream(file)));
}
public static String convert(InputSource inputSource) throws SAXException, IOException, XMLToJSONException {
ToJsonSAXHandler handler = new ToJsonSAXHandler();
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(handler);
reader.setErrorHandler(handler);
reader.parse(inputSource);
return handler.getJsonString();
}
}
測試文件 books.xml:
<?xml version="1.0" encoding="utf-8"?>
<books count="2" xmlns="https://test.org/books">
<book id="1" page="1000">
<name>Thinking in JAVA</name>
</book>
<book id="2" page="800">
<name>Core JAVA2</name>
</book>
</books>
轉換後 JSON (經 JSONObject.toString(2) 美化縮進):
{
"attrs": {"count": "2"},
"childElements": [
"\n\t",
{
"attrs": {"id": "1", "page": "1000"},
"childElements": [
"\n\t\t",
{
"attrs": {},
"childElements": ["Thinking in JAVA"],
"localName": "name",
"qName": "name",
"uri": "https://test.org/books"
},
"\n\t"
],
"localName": "book",
"qName": "book",
"uri": "https://test.org/books"
},
...
],
"localName": "books",
"qName": "books",
"uri": "https://test.org/books"
}
| 優點 | 缺點/注意事項 |
|---|---|
| 流式解析,記憶體常數級佔用,支援 GB 級大文件 | 輸出 JSON 結構冗長,保留了 XML 的混合內容 (文本節點、換行縮進) |
| 無需第三方庫,純 JDK 實現,部署零依賴 | 屬性值皆為字串,失去類型資訊 (數字、布林需自行轉換) |
| 命名空間、前綴、屬性完整保留 | 非標準 JSON 結構,需二次轉換才能符合特定 Schema |
characters 中嘗試解析數字/布林,輸出無引號值。OutputStream 而非 StringBuilder,支援超大輸出。此 SAX 轉換器為「通用、無損、低記憶體」三大特性,是處理海量 XML 遷移 JSON 的可靠基石。若業務需「乾淨」的 JSON,建議在此之上增加一層 Schema 映射轉換。