Transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8" ) НЕ работает
У меня есть следующий способ записи 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"?>...
Как вы можете ясно видеть, я устанавливаю выходной ключ ENCODING в UTF-8. Эти тесты использовались для более ранней версии Java. Я не запускал тесты через некоторое время (более года), но работает сегодня в среде Java Runtime Environment (build 1.6.0_22-b04) "Я получаю это смешное поведение.
Я проверил, что документы, вызывающие проблему, были прочитаны из файлов, которые первоначально имели такую кодировку. Кажется, что новые версии библиотек пытаются сохранить кодировку исходного файла, который был прочитан. Но это не то, что я хочу... Я действительно хочу, чтобы выход был в UTF-8.
Кто-нибудь знает какой-либо другой фактор, который может заставить трансформатор игнорировать настройку кодировки UTF-8? Есть ли что-то еще, что должно быть установлено в документе, чтобы сказать, чтобы забыть кодировку файла, который был первоначально прочитан?
UPDATE:
Я проверил один и тот же проект на другой машине, построил и проверил тесты там. На этой машине проходят все тесты! Все файлы имеют "UTF-8" в заголовке. У этой машины есть "среда выполнения Java (TM) SE Runtime Environment (сборка 1.6.0_29-b11)". Обе машины работают под управлением Windows 7. На новом компьютере, который работает правильно, jdk1.5.0_11 используется для создания сборки, но на старой машина jdk1.6.0_26 используется для создания сборки. Библиотеки, используемые для обеих сборников, абсолютно одинаковы. Может ли это быть несовместимостью JDK 1.6 с 1.5 во время сборки?
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
У меня была такая же проблема на 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);
Ответ 3
Я потратил значительное количество времени на отладку этой проблемы, потому что она хорошо работала на моей машине (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)
Ответ 4
что??
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
Ответ 5
Я мог бы обойти проблему, обернув объект Document, переданный конструктору DOMSource. Метод getXmlEncoding моей оболочки всегда возвращает значение null, все другие методы делегируются обернутому объекту Document.
Ответ 6
Я делаю здесь дикий выстрел, но вы говорите, что вы читаете файлы для данных тестов.
Можете ли вы убедиться, что вы читаете файлы с использованием правильной кодировки, поэтому, когда вы записываете в свой OutputStream, у вас уже есть данные в правильной кодировке?
Итак, есть что-то вроде нового InputStreamReader (новый FileInputStream (fileDir), "UTF8" ).
Не забывайте, что конструкторы с одним аргументом FileReader всегда используют кодировку по умолчанию для платформы: Конструкторы этого класса предполагают, что кодировка по умолчанию и значение по умолчанию размер байтового буфера соответствует.
Ответ 7
Попробуйте настроить кодировку в StreamResult специально:
StreamResult result = new StreamResult(new OutputStreamWriter(out, "UTF-8"));
Таким образом, он должен иметь возможность записывать только в UTF-8.