transformer.setOutputProperty(OutputKeys.ENCODING, “UTF-8”) 不起作用

2022-09-02 14:04:32

我有以下方法将XMLDom写入流:

public void writeToOutputStream(Document fDoc, OutputStream out) throws Exception {
    fDoc.setXmlStandalone(true);
    DOMSource docSource = new DOMSource(fDoc);
    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.setOutputProperty(OutputKeys.METHOD, "xml");
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    transformer.setOutputProperty(OutputKeys.INDENT, "no");
    transformer.transform(docSource, new StreamResult(out));
}

我正在测试其他一些 XML 功能,这只是我用来写入文件的方法。我的测试程序生成 33 个测试用例,其中文件被写出。其中 28 个具有以下标头:

<?xml version="1.0" encoding="UTF-8"?>...

但是由于某种原因,其中 1 个测试用例现在生成:

<?xml version="1.0" encoding="ISO-8859-1"?>...

还有四种产品:

<?xml version="1.0" encoding="Windows-1252"?>...

如您所见,我正在将编码输出键设置为UTF-8。这些测试曾经在早期版本的Java上工作。我有一段时间没有运行测试了(超过一年),但今天在“Java(TM)SE运行时环境(build 1.6.0_22-b04)”上运行,我得到了这个有趣的行为。

我已经验证了导致问题的文档是从最初具有这些编码的文件中读取的。似乎新版本的库正在尝试保留已读取的源文件的编码。但这不是我想要的...我真的希望输出是UTF-8。

有谁知道任何其他可能导致转换器忽略 UTF-8 编码设置的因素?是否还必须在文档上设置其他任何内容才能忘记最初读取的文件的编码?

更新:

我在另一台机器上签出了同一个项目,在那里构建并运行了测试。在那台机器上,所有的测试都通过了!所有文件的标题中都有“UTF-8”。该计算机具有“Java(TM) SE 运行时环境(生成 1.6.0_29-b11)”,两台计算机都运行 Windows 7。在工作正常的新计算机上,jdk1.5.0_11用于生成,但在旧计算机上,jdk1.6.0_26用于生成。用于两个版本的库完全相同。在构建时,JDK 1.6 是否与 1.5 不兼容?

更新:

4.5年后,Java库还是坏了,但是由于下面Vyrx的建议,我终于有了一个合适的解决方案!

public void writeToOutputStream(Document fDoc, OutputStream out) throws Exception {
    fDoc.setXmlStandalone(true);
    DOMSource docSource = new DOMSource(fDoc);
    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.setOutputProperty(OutputKeys.METHOD, "xml");
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.setOutputProperty(OutputKeys.INDENT, "no");
    out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>".getBytes("UTF-8"));
    transformer.transform(docSource, new StreamResult(out));
}

解决方案是禁用标头的写入,并在将 XML 序列化到输出蒸汽之前写入正确的标头。跛行,但它产生正确的结果。4年前中断的测试现在再次运行!


答案 1

在序列化表情符号字符时,我在Android上遇到了同样的问题。在转换器中使用 UTF-8 编码时,输出是 HTML 字符实体(UTF-16 代理项对),这随后会中断读取数据的其他解析器。

这就是我最终解决它的方式:

StringWriter sw = new StringWriter();
sw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
Transformer t = TransformerFactory.newInstance().newTransformer();

// this will work because we are creating a Java string, not writing to an output
t.setOutputProperty(OutputKeys.ENCODING, "UTF-16"); 
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.transform(new DOMSource(elementNode), new StreamResult(sw));

return IOUtils.toInputStream(sw.toString(), Charset.forName("UTF-8"));

答案 2

要回答这个问题,下面的代码对我有用。这可以采用输入编码并将数据转换为输出编码。

        ByteArrayInputStream inStreamXMLElement = new ByteArrayInputStream(strXMLElement.getBytes(input_encoding));
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder(); 
        Document docRepeat = db.parse(new InputSource(new InputStreamReader(inStreamXMLElement, input_encoding)));
        Node elementNode = docRepeat.getElementsByTagName(strRepeat).item(0);

        TransformerFactory tFactory = null;
        Transformer transformer = null;
        DOMSource domSourceRepeat = new DOMSource(elementNode);
        tFactory = TransformerFactory.newInstance();
        transformer = tFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, output_encoding);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        StreamResult sr = new StreamResult(new OutputStreamWriter(bos, output_encoding));


        transformer.transform(domSourceRepeat, sr);
        byte[] outputBytes = bos.toByteArray();
        strRepeatString = new String(outputBytes, output_encoding);

推荐