XML 解析器默認解析所有文本內容。若文本包含 <、& 等特殊字符,會被誤認為標記開始導致解析錯誤。解決方案有二:
<、>、&、'、"。僅 < 和 & 強制要求轉義,但建議全部使用以減少錯誤。<![CDATA[ 開始,]]> 結束。內部所有字符原樣保留,解析器完全忽略,適合程序代碼、腳本等包含大量特殊字符的內容。注意:CDATA 不可嵌套;內容不得包含 ]]> 或 <![CDATA[;標記間不可有空格換行。
使用 Dom4j XMLWriter 格式化輸出時,默認會對所有元素文本內容進行轉義,導致精心構造的 CDATA 區塊被雙重轉義:
<!-- 原始輸入 -->
<addr><![CDATA[經三路<鑫苑>19F]]></addr>
<!-- Dom4j 默認格式化輸出 -->
<toname><![CDATA[經三路<鑫苑>19F]]></toname>
這顯然不是預期結果——CDATA 區塊本身不應被轉義。
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><![CDATA[經三路<鑫苑>19F]]></toname></person>
-------------------------
<?xml version="1.0" encoding="GBK"?>
<person>
<toname><![CDATA[經三路<鑫苑>19F]]></toname>
</person>
-------------------------
<?xml version="1.0" encoding="GBK"?>
<person>
<toname><![CDATA[經三路<鑫苑>19F]]></toname>
</person>
第三種輸出(setEscapeText(false))完美保留 CDATA 區塊原貌,達到預期效果。
true)防止注入攻擊;確需輸出 CDATA 區塊時關閉轉義(false)。isTrans 參數的格式化工具方法,調用處顯式聲明意圖。<![CDATA[...]]> 字符串作為元素文本。OutputFormat.setEncoding() 與 XML 聲明編碼一致(示例中均為 GBK)。