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"?>...
さらに4つの農産物:
<?xml version="1.0" encoding="Windows-1252"?>...
ご覧のとおり、ENCODING出力キーをUTF-8に設定しています。これらのテストは、以前のバージョンのJavaで機能していました。私はしばらく(1年以上)テストを実行していませんが、今日「Java(TM)SEランタイム環境(ビルド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を出力Steamにシリアル化する直前に正しいヘッダーを書き込むことです。ラメ、しかしそれは正しい結果を生み出します。 4年以上前に破られたテストが再び実行されています!
私は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"));
質問に答えるには、次のコードが役立ちます。これは、入力エンコーディングを取り、データを出力エンコーディングに変換できます。
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);
この問題は自分のマシンでうまく機能していたため(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"));
しかし、これは期待通りに機能しました
val out = new StringWriter()
val result = new StreamResult(out)
DOMSourceコンストラクターに渡されたDocumentオブジェクトをラップすることで問題を回避できます。ラッパーのメソッドgetXmlEncodingは常にnullを返します。他のすべてのメソッドは、ラップされたDocumentオブジェクトに委任されます。
何について?:
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