首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

XML 轉 JSON 實戰:基於 SAX 解析器的通用轉換器 Java 實作

     編輯:Java 綜合教學

XML 轉 JSON 實戰:基於 SAX 解析器的通用轉換器 Java 實作

在系統整合、配置遷移、異構數據交換等場景中,將 XML 文檔轉換為 JSON 格式是常見需求。相較於 DOM 解析需將整棵樹載入記憶體,SAX (Simple API for XML) 採用事件驅動的流式解析,記憶體佔用極低,適合處理大型 XML 文件。本文提供一套完整、可生產環境使用的 XMLToJSON 工具類,核心包含三個類:事件處理器 ToJsonSAXHandler、異常類 XMLToJSONException、門面類 XMLToJSON。

架構設計

  • ToJsonSAXHandler:繼承 DefaultHandler,處理 startElement、endElement、characters 等 SAX 事件,維護一個 StringBuilder 構建 JSON 字串。關鍵狀態標記:isProcessing (是否正在解析)、justProcessStartElement (判斷是否需輸出逗號分隔)。
  • XMLToJSONException:封裝轉換過程中可能拋出的異常 (如未解析完成就調用 getJsonString)。
  • XMLToJSON:工具門面,提供 convert(String)、convert(File)、convert(InputSource) 三種重載,內部創建 XMLReader 並註冊 Handler。

核心代碼精講

1. ToJsonSAXHandler - 事件處理邏輯

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();
    }
}

2. XMLToJSON - 統一入口

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 映射轉換。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved