
本文详解如何通过 stax2 的 `p_attr_value_escaper` 属性配合自定义 `escapingwriterfactory`,实现对 xml 属性值中预转义字符(如 ` `)的“零干预”输出,避免被二次转义为 ` `。
在使用标准 javax.xml.stream.XMLStreamWriter 或默认 Stax2 实现生成 XML 时,一个常见痛点是:当开发者主动传入已按 XML 规范转义的字符串(例如 "This That",其中 表示换行符的字符引用),写入器仍会将其视为普通文本,对其中的 & 符号进行额外转义,最终生成 —— 这破坏了原始意图,导致解析时无法还原为换行符。
Stax2(特别是 Woodstox 实现)提供了精细的转义控制能力,其关键在于 XMLOutputFactory2.P_ATTR_VALUE_ESCAPER 属性。但需特别注意:该属性不接受布尔值(如 true/false),而必须绑定一个实现了 org.codehaus.stax2.io.EscapingWriterFactory 接口的工厂类实例。错误地传入 Boolean 是引发 ClassCastException 的根本原因(如题中所示)。
✅ 正确用法:实现并注册自定义 EscapingWriterFactory
核心思路是提供一个“透传式”工厂——它创建的 Writer 不做任何额外转义,直接将原始字符序列写入底层流。以下是一个简洁、安全的 Scala 实现:
import org.codehaus.stax2.XMLOutputFactory2
import org.codehaus.stax2.io.EscapingWriterFactory
import javax.xml.stream.XMLStreamWriter
import java.io.{File, FileOutputStream, Writer, OutputStream}
import java.lang.IllegalArgumentException
class CustomXmlEscapingWriterFactory extends EscapingWriterFactory {
override def createEscapingWriterFor(writer: Writer, encoding: String): Writer =
new Writer {
override def write(cbuf: Array[Char], off: Int, len: Int): Unit =
writer.write(cbuf, off, len)
override def flush(): Unit = writer.flush()
override def close(): Unit = writer.close()
}
override def createEscapingWriterFor(outputStream: OutputStream, encoding: String): Writer =
throw new IllegalArgumentException("OutputStream-based escaping not supported in this implementation")
}
// 使用示例
val file = new File("stax2test.xml")
val fos = new FileOutputStream(file)
val outputFactory = XMLOutputFactory2.newInstance().asInstanceOf[XMLOutputFactory2]
// ✅ 关键:传入工厂类的实例,而非 Boolean!
outputFactory.setProperty(
XMLOutputFactory2.P_ATTR_VALUE_ESCAPER,
new CustomXmlEscapingWriterFactory()
)
val writer = outputFactory.createXMLStreamWriter(fos)
try {
writer.writeStartDocument()
writer.writeStartElement("elem1")
writer.writeAttribute("att1", "This
That") // 期望原样输出
writer.writeEndElement()
writer.writeEndDocument()
} finally {
writer.close()
fos.close()
}执行后,生成的 XML 文件内容为:
完全符合预期 —— 未被二次转义。
⚠️ 重要注意事项
- 工厂实例化时机:务必在调用 createXMLStreamWriter(...) 之前 设置属性,否则无效。
- 编码一致性:createEscapingWriterFor 方法接收 encoding 参数,应确保与 XMLOutputFactory 配置的编码(如 UTF-8)一致;本例中因透传,可忽略该参数,但生产环境建议校验。
- OutputStream 支持:示例中禁用了 OutputStream 版本的工厂方法(抛出异常)。若需支持字节流(如直接写入 FileOutputStream),需在 createEscapingWriterFor(OutputStream, ...) 中返回一个基于 OutputStreamWriter 的 Writer,并指定正确编码。
- 依赖版本:确保使用兼容的 Woodstox 版本(如 woodstox-core 6.4+)及 Stax2 API(stax2-api 4.2+),旧版本接口可能有差异。
- 适用场景:此方案适用于明确知晓输入字符串已合规转义的场景。若输入为原始未转义文本(如含 &,
? 总结
Stax2 的 P_ATTR_VALUE_ESCAPER 是解决 XML 属性值精细化转义需求的利器。成功的关键在于理解其设计契约:它要求一个 EscapingWriterFactory 实例,而非开关标志。通过实现一个“无操作”(no-op)工厂,即可让写入器信任并原样输出已转义内容。这不仅解决了 类问题,也为更复杂的自定义转义逻辑(如条件转义、特定字符白名单等)提供了清晰的扩展路径。










