Dom4j写XML需同时设置setIndent(true)和setNewlines(true)才生效,仅设前者无效;推荐用OutputFormat.createPrettyPrint(),自定义缩进需手动设setIndent(" "),注意编码与BOM问题及缓冲区未刷新风险。

Dom4j写XML不缩进?关键是OutputFormat的setIndent和setNewlines必须同时开
Dom4j默认输出是单行无缩进的,不是bug,是设计如此。只调setIndent(true)没用,必须配合setNewlines(true),否则换行符被吞掉,缩进就失效。
常见错误现象:document.asXML()返回一整行长字符串,连根节点都挤在一行;或者只在开头空几格,子节点完全没对齐。
-
OutputFormat format = OutputFormat.createPrettyPrint();是最简方案,它内部已设好indent=true、newlines=true、encoding="UTF-8" - 如果要自定义缩进字符(比如用4个空格而非2个),得手动建
OutputFormat再设:format.setIndent(" "); - 注意:
createPrettyPrint()默认用2个空格,且会自动在<?xml?>后加空行——有些老系统校验严格,可能报“XML开头有空白”
中文乱码或BOM头问题:OutputFormat的encoding和Writer编码必须一致
即使OutputFormat设了encoding="UTF-8",如果用FileWriter直接写文件,Java默认用系统编码(Windows上是GBK),结果XML里中文变问号或方块。
使用场景:生成配置文件、导出数据给前端解析、和遗留系统对接。
立即学习“Java免费学习笔记(深入)”;
- 绝对不要用
FileWriter,改用OutputStreamWriter+FileOutputStream,显式指定"UTF-8" -
OutputFormat的encoding字段只控制XML声明里的encoding="UTF-8",不负责实际写入编码 - 如果目标系统讨厌BOM,避免用
new OutputStreamWriter(new FileOutputStream(f), "UTF-8")——Java 8+的"UTF-8"默认带BOM,应改用"UTF-8"+StandardCharsets.UTF_8(Java 7需用new OutputStreamWriter(..., new BOMOutputStream(...)))
性能敏感时别用createPrettyPrint():格式化开销比纯文本高3–5倍
批量导出上千份XML时,createPrettyPrint()会逐节点计算深度、拼接空格、插入换行,CPU和内存占用明显上升。线上服务曾因此GC频率翻倍。
参数差异:createCompactFormat()几乎零开销,但输出不可读;createPrettyPrint()适合调试或人工查看的场景。
- 生产环境导出日志/备份XML,优先用
createCompactFormat(),靠外部工具(如xmllint --format)事后美化 - 如果必须实时格式化,把
OutputFormat实例缓存复用,别每次new一个——它的setIndent()等方法不是线程安全的,多线程共用需同步 - DOM树特别深(>10层)时,
setIndent(true)会导致StringBuilder频繁扩容,可预估最大深度设format.setIndent(" "); format.setLineWidth(0);关掉自动折行
写入失败却没报错?检查XMLWriter是否close()或flush()
Dom4j的XMLWriter是缓冲写入,不close()或flush(),最后一段内容可能卡在缓冲区,文件看起来“写了一半”或干脆为空。
容易踩的坑:用try-with-resources时忘了XMLWriter实现了AutoCloseable,但某些旧版dom4j(如1.6.1)的close()有bug,会漏刷缓冲区。
- 务必用try-with-resources包裹
XMLWriter,确保close()执行 - 如果用的是dom4j 1.6.x,
close()前手动加writer.flush()更稳妥 - 错误信息示例:
java.io.IOException: Stream closed——说明close()被调了两次,可能是try-with-resources和手动close()混用
缩进这事看着小,但setIndent和setNewlines的耦合性、Writer的缓冲机制、编码与BOM的隐式依赖,三者叠在一起,调试时很容易绕晕。真遇到不生效,先盯住这两行:format.setIndent(true); format.setNewlines(true);,再查Writer有没有关。其他都是后话。










