трансформатор.setOutputProperty(OutputKeys.Кодировка, "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"?>...

как вы можете ясно видеть, я установка выходного ключа кодирования в UTF-8. Эти тесты использовались для работы на более ранней версии Java. Я не запускал тесты некоторое время(более года), но сегодня работает на "Java (TM) SE Runtime Environment (build 1.6.0_22-b04)", я получаю это забавное поведение.

Я проверил, что документы, вызывающие проблему, были прочитаны из файлов, которые первоначально имели эту кодировку. Похоже, что новые версии библиотек пытаются сохранить кодировку исходного файла, был прочитан. Но это не то, чего я хочу ... Я действительно хочу, чтобы выход был в UTF-8.

кто-нибудь знает о каком-либо другом факторе, который может заставить трансформатор игнорировать настройку кодировки UTF-8? Есть ли что-то еще, что должно быть установлено в документе, чтобы сказать, чтобы забыть кодировку файла, который был первоначально прочитан?

обновление:

Я проверил тот же проект на другой машине, построил и провел там тесты. На этой машине все тесты проходят! Все файлы имеют "UTF-8" в заголовке. Эта машина имеет" Java(TM) SE Runtime Environment (build 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 лет назад, теперь снова работают!

7 ответов


У меня была такая же проблема на Android при сериализации символов emoji. При использовании кодировки 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)

о чем?:

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

Я мог бы обойти проблему, обернув объект документа, переданный конструктору DOMSource. Метод getXmlEncoding моей оболочки всегда возвращает null, все остальные методы делегируются объекту обернутого документа.


Я делаю дикий выстрел здесь, но вы упоминаете, что читаете файлы для данных тестов. Можете ли вы убедиться, что Вы читаете файлы, используя правильную кодировку, чтобы при записи в OutputStream у вас уже были данные в правильной кодировке?

Итак, что-то вроде new InputStreamReader(новый FileInputStream(fileDir), "UTF8").

Не забывайте, что одно аргументные конструкторы FileReader всегда используют кодировку платформы по умолчанию : конструкторы этого класса предполагают, что кодировка по умолчанию и Байт размер буфера по умолчанию являются подходящими.


попробуйте установить кодировку на вашем StreamResult конкретно:

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

таким образом, он должен иметь возможность писать только в UTF-8.