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

Dom4j XML 格式化輸出:CDATA 區塊轉義問題完美解決方案

     編輯:Java 相關知識

XML CDATA 區塊基礎知識回顧

XML 解析器默認解析所有文本內容。若文本包含 <、& 等特殊字符,會被誤認為標記開始導致解析錯誤。解決方案有二:

  1. 實體引用:&lt;、&gt;、&amp;、&apos;、&quot;。僅 < 和 & 強制要求轉義,但建議全部使用以減少錯誤。
  2. CDATA 區塊:以 <![CDATA[ 開始,]]> 結束。內部所有字符原樣保留,解析器完全忽略,適合程序代碼、腳本等包含大量特殊字符的內容。

注意:CDATA 不可嵌套;內容不得包含 ]]> 或 <![CDATA[;標記間不可有空格換行。

Dom4j 格式化輸出的轉義陷阱

使用 Dom4j XMLWriter 格式化輸出時,默認會對所有元素文本內容進行轉義,導致精心構造的 CDATA 區塊被雙重轉義:

<!-- 原始輸入 -->
<addr><![CDATA[經三路<鑫苑>19F]]></addr>

<!-- Dom4j 默認格式化輸出 -->
<toname>&lt;![CDATA[經三路&lt;鑫苑&gt;19F]]&gt;</toname>

這顯然不是預期結果——CDATA 區塊本身不應被轉義。

核心解決方案:XMLWriter.setEscapeText(false)

Dom4j 提供 XMLWriter.setEscapeText(boolean) 方法控制是否轉義文本內容。關鍵代碼:

public static String formatXml(Document document, String charset, boolean isTrans) {
    OutputFormat format = OutputFormat.createPrettyPrint();
    format.setEncoding(charset);
    StringWriter sw = new StringWriter();
    XMLWriter xw = new XMLWriter(sw, format);
    xw.setEscapeText(istrans); // 關鍵:false 禁用轉義
    try {
        xw.write(document);
        xw.flush();
        xw.close();
    } catch (IOException e) { ... }
    return sw.toString();
}

完整驗證示例

public class Person {
    private String name, addr;
    public Person(String name, String addr) { this.name = name; this.addr = addr; }

    public static void main(String[] args) {
        new Person("張三", "經三路<鑫苑>19F").showXml();
    }

    public void showXml() {
        Document doc = DocumentHelper.createDocument();
        doc.setXMLEncoding("GBK");
        Element root = doc.addElement("person");
        // 關鍵:手動構造包含 CDATA 標記的文本內容
        addElement(root, "toname", "<![CDATA[" + this.addr + "]]>");
        
        String xml1 = doc.asXML(); // 默認轉義
        String xml2 = formatXml(doc, "GBK", true);  // 開啟轉義
        String xml3 = formatXml(doc, "GBK", false); // 禁用轉義
        
        System.out.println(xml1); System.out.println("-------------------------");
        System.out.println(xml2); System.out.println("-------------------------");
        System.out.println(xml3);
    }

    public static Element addElement(Element e, String name, Object value) {
        Element x = e.addElement(name);
        if (value == null || "".equals(value.toString().trim())) x.setText("");
        else x.setText(value.toString()); // 直接設置文本,不自動處理 CDATA
        return x;
    }
    // formatXml 方法同上...
}

輸出結果對比

<?xml version="1.0" encoding="GBK"?>
<person><toname>&lt;![CDATA[經三路&lt;鑫苑&gt;19F]]&gt;</toname></person>
-------------------------
<?xml version="1.0" encoding="GBK"?>
<person>
  <toname>&lt;![CDATA[經三路&lt;鑫苑&gt;19F]]&gt;</toname>
</person>
-------------------------
<?xml version="1.0" encoding="GBK"?>
<person>
  <toname><![CDATA[經三路<鑫苑>19F]]></toname>
</person>

第三種輸出(setEscapeText(false))完美保留 CDATA 區塊原貌,達到預期效果。

最佳實踐建議

  1. 區分場景:普通文本內容建議啟用轉義(true)防止注入攻擊;確需輸出 CDATA 區塊時關閉轉義(false)。
  2. 封裝工具方法:提供帶 isTrans 參數的格式化工具方法,調用處顯式聲明意圖。
  3. 手動構造 CDATA 文本:Dom4j 無原生 CDATA 節點類型,需開發者手動拼接 <![CDATA[...]]> 字符串作為元素文本。
  4. 編碼一致性:確保 OutputFormat.setEncoding() 與 XML 聲明編碼一致(示例中均為 GBK)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved