使用 JDOM XMLOutputter 輸出 XML 時,控制台或文件出現亂碼,即使調用 format.setEncoding("UTF-8") 也無效。網上流傳各種「設置 Format」「加 BOM」偏方,實則治標不治本。
XMLOutputter 提供兩組輸出方法:
output(Document, OutputStream) — 字節流,**由 JDOM 內部按指定編碼寫入**output(Document, Writer) — 字符流,**編碼由 Writer 構造時決定**,JDOM 無法干預核心誤區:以為 Format.setEncoding() 能控制 Writer 輸出編碼。實際上,Writer 一旦構造完成,其編碼已鎖死(FileWriter 默認平台編碼,PrintWriter(System.out) 默認控制台編碼),JDOM 僅將字符傳給 Writer,無法改變其編碼行為。
// ❌ 錯誤:FileWriter 默認平台編碼(Windows 通常是 GBK),導致 UTF-8 字符被錯誤編碼
FileWriter fw = new FileWriter("out.xml");
outputter.output(doc, fw);
// ✅ 正確:FileOutputStream 由 JDOM 按 Format 指定編碼寫入
FileOutputStream fos = new FileOutputStream("out.xml");
outputter.output(doc, fos); // 此時 setEncoding("UTF-8") 生效
// ❌ 錯誤:System.out 是 PrintStream,編碼為控制台編碼(Windows cmd 為 GBK)
outputter.output(doc, System.out);
// ✅ 正確:PrintWriter 构造时指定編碼,或使用不轉碼的 PrintWriter
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"), true);
outputter.output(doc, pw);
// 簡化寫法(JDK 1.5+):PrintWriter 支持直接指定編碼
PrintWriter pw = new PrintWriter(System.out, true, "UTF-8"); // autoFlush=true
outputter.output(doc, pw);
StringWriter sw = new StringWriter();
outputter.output(doc, sw); // 字符串無編碼,直接獲取內容
String xml = sw.toString();
public class JdomUtf8Demo {
public static void main(String[] args) throws Exception {
File xmlFile = new File("test.xml");
Element root = new Element("root");
root.addContent(new Element("item").setText("中文測試"));
Document doc = new Document(root);
XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
// Format 默認編碼即 UTF-8,無需顯式 setEncoding
// 文件輸出:用 FileOutputStream
try (FileOutputStream fos = new FileOutputStream(xmlFile)) {
outputter.output(doc, fos);
}
// 控制台輸出:用指定編碼的 PrintWriter
try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"), true)) {
outputter.output(doc, pw);
}
}
}
FileWriter 繼承 OutputStreamWriter,構造時**強制使用平台默認編碼**(Charset.defaultCharset()),且**無構造參數可覆蓋**。JDK 設計如此是為了「開箱即用」,但犧牲了編碼控制權。任何需要指定編碼的文件寫入,均應直接用 FileOutputStream + OutputStreamWriter 或 JDOM 直接接受 OutputStream。
文件用 Stream,控制台用 Writer(指定編碼),字符串用 StringWriter。
Format.setEncoding() 只對 OutputStream 生效,對 Writer 無效。
徹底理解 JDK 流體系編碼邊界,JDOM UTF-8 輸出不再是玄學。