2013-03-23 192 views
11

我有以下的方法写一个XMLDOM到流:transformer.setOutputProperty(OutputKeys.ENCODING,“UTF-8”)不工作

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"?>... 

,你可以清楚地看到,我正在将ENCODING输出密钥设置为UTF-8。这些测试曾用于早期版本的Java。我有一段时间没有运行测试(超过一年),但今天在“Java(TM)SE运行时环境(build 1.6.0_22-b04)”上运行。“我得到了这个有趣的行为。

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

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

UPDATE:

我检查了同一个项目从另一台机器上,建造和运行测试那里。在那台机器上所有的测试都通过了!所有的文件头都有“UTF-8”。该机器具有“Java(TM)SE运行时环境(内部版本1.6.0_29-b11)”两台机器都运行Windows 7.在正常工作的新机器上,使用jdk1.5.0_11来构建版本,但是旧版本机器jdk1.6.0_26用于构建。用于两个版本的库完全一样。它是否可以在构建时与JDK 1.6不兼容?

UPDATE:

后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

这的确看起来像一些错误或不兼容的问题。没有可重复的测试用例,任何人都不可能提供帮助。你能否提供一个[SSCCE](http://sscce.org/),并列出工具/库的所有版本? – sleske 2013-05-19 08:17:21

+0

有几个地方可以检查您的语言环境。您的本地计算机具有区域设置,您的IDE可能具有区域设置,并且您的JVM进程具有区域设置。在我的Locale更改之前,我已经看到类似这样的问题。你如何运行测试? java.exe,maven,IDE? – 2013-06-10 11:50:50

+0

由于我已经直接指定了UTF-8,所以语言环境应该没有问题,但要直接回答您的问题,测试代码将作为调用Java.exe的命令行调用位于美国太平洋海岸的Windows系统上并针对美国英语和太平洋时区进行配置。 – AgilePro 2013-06-14 01:30:01

回答

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")); 
+0

是的,看起来像是有效的。我不喜欢将我的整个XML树转换为内存中的字符串(特别是在StringWriter效率不高的情况下)。我真的坚持直接输出到输出。一种可能的解决方案是在序列化之后添加头,而不是在没有头的序列化XML到相同的输出流之前将头写入输出流。我会看看这是否有效。 – AgilePro 2017-12-09 17:53:00

+1

我已经重写了这个想法,正确使用流并给你答案的功劳。 (谢谢!)正如你写的那样,你将同时在内存中拥有三份文档副本。对于小型XML而言不成问题,但通常在内存中有三个重要数据文件副本效率不高。更好的方法是在将XML序列化到writer之前简单地编写头文件。我重写了你的答案,使它在内存中只有2个XML副本。 – AgilePro 2017-12-09 18:16:12

-1

我在这里拍摄一张照片,但是您提到您正在读取测试数据的文件。 您是否可以确保您使用正确的编码读取文件,因此当您向OutputStream中写入数据时,您已经拥有正确编码的数据?

因此,有像新的InputStreamReader(新的FileInputStream(fileDir),“UTF8”)的东西。

不要忘记的FileReader的单参数的构造函数总是使用平台默认编码:The constructors of this class assume that the default character encoding and the default byte-buffer size are appropriate.

+0

我从来没有使用FileReader。 --- DOM“Document”使用字符串值,这意味着它们已经从原始形式转换而来。我正在使用Java DOM实用程序直接从字节流中读取文件。预计该流将根据指定编码的XML标头进行解释。这就是XML的工作原理。 ---该文件似乎被正确读取,并且以指定的编码写入 - 而不是我要求写入的编码。 – AgilePro 2013-09-24 23:16:28

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); 
+0

仅在某些版本的Java中出现此错误。我没有时间对究竟是什么环境导致问题进行全面调查,甚至没有时间在这里发布测试代码,但它与您发布的内容非常相似。失败的原因是已经运行多年的自动化测试。你所包含的代码看起来就像是一个如何测试问题的好例子。我不知道我是否能够回到失败的原始环境,并在那里重新运行测试。所有,在时间的充裕... – AgilePro 2014-04-16 19:12:01

-1

尝试专门设置你的StreamResult编码:

StreamResult result = new StreamResult(new OutputStreamWriter(out, "UTF-8")); 

这样,它应该只能够在UTF-8写出来。

+2

问题是'头'是不正确的。如果标题表示它是ISO-8859-1,那么我不希望它以其他方式实际编码。我需要标题和流的实际编码。这就是为什么使用这些库我总是使用输入/输出流而不使用读写器......因为标准说你必须读取头才能找出编码是什么。 – AgilePro 2014-11-04 21:48:44

0

怎么样?:

public static String documentToString(Document doc) throws Exception{ return(documentToString(doc,"UTF-8")); }// 
    public static String documentToString(Document doc, String encoding) throws Exception{ 
    TransformerFactory transformerFactory =TransformerFactory.newInstance(); 
    Transformer transformer = null; 

if ("".equals(validateNullString(encoding))) encoding = "UTF-8"; 
try{ 
    transformer = transformerFactory.newTransformer(); 
    transformer.setOutputProperty(OutputKeys.INDENT, "yes") ; 
    transformer.setOutputProperty(OutputKeys.ENCODING, encoding) ; 
}catch (javax.xml.transform.TransformerConfigurationException error){ 
    return null; 
} 

Source source = new DOMSource(doc);  
StringWriter writer = new StringWriter(); 
Result result = new StreamResult(writer); 

try{ 
    transformer.transform(source,result); 
}catch (javax.xml.transform.TransformerException error){ 
    return null; 
} 
return writer.toString();  
}//documentToString 
1

我花了很多时间显著量调试这个问题,因为这是在我的机器上(Ubuntu的14 +的Java 1.8.0_45)工作良好,但在正常不工作生产(Alpine Linux + Java 1.7)。

与我的预期相反,从上面提到的答案没有帮助。

ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
StreamResult sr = new StreamResult(new OutputStreamWriter(bos, "UTF-8")); 

但是这一个通过包装传递给DOMSource的构造Document对象和预期一样

val out = new StringWriter() 
val result = new StreamResult(out) 
0

我可以解决该问题。我的包装器的getXmlEncoding方法总是返回null,所有其他方法都委托给包装好的Document对象。