我刚刚遇到了同样的问题,经过很长时间的研究,这就是我得出的结论。
Java XSLT 处理器将多字节 UTF-8 字符转义为 HTML 实体,即使输出模式是 XML ......如果多字节字符出现在未包装在 CDATA 中的 text() 节点中。如果字符包含在 CDATA(用于输出)中,则多字节字符将被保留。
我的问题:
我有一个看起来像这样的 xml 文件,并带有表情符号。
<events>
<event>
<id>RANDOMID</id>
<blah>
<blahId>FOOONE</blahId>
</blah>
<blah>
<blahId>FOOTWO</blahId>
</blah>
<eventComment>Did some things. Had some Fun. </eventComment>
</event>
</events>
我从一个看起来像这样的 XSL 样式表开始:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict"
>
<xsl:output method = "xml" version="1.0" encoding = "UTF-8" omit-xml-declaration="no" indent="yes" />
<xsl:template match="/">
<events>
<xsl:for-each select="/events/event">
<event>
<xsl:copy-of select="./*[name() != 'blah'"/>
<xsl:for-each select="./blah">
<blahId><xsl:copy-of select="./blahId/text()"/></blahId>
</xsl:for-each>
</event>
</xsl:for-each>
</events>
</xsl:template>
</xsl:stylesheet>
用一个 java Transformer 运行它,它始终��
会在我的表情符号应该出现的地方产生。随后尝试解析生成的 Document 失败,并出现以下异常消息:
org.xml.sax.SAXParseException; lineNumber: y; columnNumber: x; Character reference "�" is an invalid XML character.
霍华什!
xsltproc
在命令行上测试它是没有用的,因为xsltproc
在多字节字符方面并不愚蠢。我得到了我期望的输出。
一个解法
通过在标记属性中指定 QName让 XSLT 包装eventComment
in CDATA将保留字节并与 xsltproc和 java Transformer一起使用。xsl:output
cdata-section-elements
这里的神奇之处在于标签的输出cdata-secion-elements
属性。https://www.w3.org/TR/xslt#output<xsl:output>
我将我的 XSL 模板更新为:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict"
>
<xsl:output cdata-section-elements="eventComment" method="xml" version="1.0" encoding="UTF-8" omit-xml-declaration="no" indent="yes"/>
<xsl:template match="/">
<events>
<xsl:for-each select="/events/event">
<event>
<xsl:copy-of select="./*[name() != 'blah' and name() != 'eventComment']"/>
<!-- For the cdata-section-elements to resolve that eventComment needs to be preserved as CDATA
(so we don't get java doing stupid things with unicode escapment)
it needs to be explicitly referenced here.
-->
<eventComment><xsl:copy-of select="./eventComment/text()"/></eventComment>
<xsl:for-each select="./blah">
<blahId><xsl:copy-of select="./blahId/text()"/></blahId>
</xsl:for-each>
</event>
</xsl:for-each>
</events>
</xsl:template>
</xsl:stylesheet>
现在我的输出xsltproc
和 java Transformer 看起来像这样,并且可以使用 java DocumentBuilders 愉快地解析。
<?xml version="1.0" encoding="UTF-8"?>
<events xmlns="http://www.w3.org/TR/xhtml1/strict">
<event>
<id xmlns="">RANDOMID</id>
<eventComment><![CDATA[Did some things. Had some Fun. ]]></eventComment>
<blahId>FOO</blahId>
<blahId>FOOTOO</blahId>
</event>
</events>