4

我的 XSLT 转换已经成功了几个月,直到我遇到了一个带有 Unicode 字符(很可能是表情符号)的 XML 文件。我需要保留 Unicode,但 XSLT 正在将其转换为 HTML 实体。我认为将编码设置为 UTF-8 可以解决我的问题,但我仍然遇到问题。

任何帮助表示赞赏。代码:

private byte[] transform(InputStream stream) throws Exception{
    System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl"); 

    Transformer xmlTransformer;

    xmlTransformer = (TransformerImpl) TransformerFactory.newInstance().newTransformer(new   StreamSource(createXsltStylesheet()));
    xmlTransformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

    XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(stream,"UTF-8");
    Source staxSource = new StAXSource(reader, true); 
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    Writer writer = new OutputStreamWriter(outputStream, "UTF-8");
    xmlTransformer.transform(staxSource, new StreamResult(writer));


    return outputStream.toByteArray();
}

如果我添加

xmlTransformer.setOutputProperty(OutputKeys.METHOD, "text");

Unicode 被保留,但 XML 没有。

4

4 回答 4

1

刚刚遇到了同样的问题,经过很长时间的研究,这就是我得出的结论。

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 运行它,它始终&#55357;&#56397;会在我的表情符号应该出现的地方产生。随后尝试解析生成的 Document 失败,并出现以下异常消息:

org.xml.sax.SAXParseException; lineNumber: y; columnNumber: x; Character reference "&#55357" is an invalid XML character.

霍华什!

xsltproc在命令行上测试它是没有用的,因为xsltproc在多字节字符方面并不愚蠢。我得到了我期望的输出。

一个解法

通过在标记属性中指定 QName让 XSLT 包装eventCommentin CDATA将保留字节并与 xsltproc和 java Transformer一起使用。xsl:outputcdata-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>
于 2016-08-26T20:56:38.373 回答
0

尝试使用 Apache Serializer 将 XML 转换为字符串。

//Serialize DOM
OutputFormat format    = new OutputFormat (doc); 
// as a String
StringWriter stringOut = new StringWriter ();    
XMLSerializer serial   = new XMLSerializer (stringOut, 
                                                  format);
serial.serialize(doc);
// Display the XML
System.out.println(stringOut.toString());
于 2014-04-09T10:34:59.073 回答
0

这条线很可疑:

stream = IOUtils.toInputStream(outputStream.toString(),"UTF-8");

您正在ByteArrayOutputStream使用平台的默认编码将 a 转换为字符串,这可能不是 UTF-8。将其更改为

stream = IOUtils.toInputStream(outputStream.toString("UTF-8"),"UTF-8");

或者,为了获得更好的性能,只需将字节数组包装在 a 中ByteArrayInputStream

return new ByteArrayInputStream(outputStream.toByteArray());
于 2013-08-07T08:30:10.270 回答
0

刚刚通过在原始 XML 中添加以下行解决了类似的问题: document.appendChild(document.createProcessingInstruction(StreamResult.PI_DISABLE_OUTPUT_ESCAPING, ""));

参考:在 JAVA 中将表情符号写入 XML 文件

也许可以对变压器使用类似的设置...

于 2020-03-01T15:23:52.153 回答