it-swarm.dev

transformer.setOutputProperty (OutputKeys.ENCODING, "UTF-8") NÃO está funcionando

Eu tenho o seguinte método para escrever um XMLDom para um fluxo:

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));
}

Eu estou testando algumas outras funcionalidades XML, e isso é apenas o método que eu uso para gravar em um arquivo. Meu programa de teste gera 33 casos de teste onde os arquivos são gravados. 28 deles têm o seguinte cabeçalho:

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

Mas, por algum motivo, 1 dos casos de teste agora produz:

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

E mais quatro produtos:

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

Como você pode ver claramente, estou configurando a chave de saída ENCODING para UTF-8. Esses testes costumavam funcionar em uma versão anterior do Java. Eu não corri os testes em um tempo (mais de um ano), mas correndo hoje em "Java (TM) SE Runtime Environment (compilação 1.6.0_22-b04)" Eu recebo este comportamento engraçado.

Verifiquei que os documentos que causaram o problema foram lidos de arquivos que originalmente tinham essa codificação. Parece que as novas versões das bibliotecas estão tentando preservar a codificação do arquivo de origem que foi lido. Mas isso não é o que eu quero ... Eu realmente quero que a saída seja em UTF-8. 

Alguém sabe de algum outro fator que possa fazer com que o transformador ignore a configuração de codificação UTF-8? Há mais alguma coisa que precisa ser definida no documento para esquecer a codificação do arquivo que foi originalmente lido?

ATUALIZAR:

Eu verifiquei o mesmo projeto em outra máquina, construí e executei os testes lá. Naquela máquina todos os testes passam! Todos os arquivos têm "UTF-8" no cabeçalho. Essa máquina possui o "Java (TM) SE Runtime Environment (build 1.6.0_29-b11)" As duas máquinas estão executando o Windows 7. Na nova máquina que funciona corretamente, o jdk1.5.0_11 é usado para fazer a compilação, mas na antiga A máquina jdk1.6.0_26 é usada para fazer a construção. As bibliotecas usadas para as duas construções são exatamente as mesmas. Pode ser uma incompatibilidade do JDK 1.6 com 1.5 no tempo de compilação?

ATUALIZAR:

Após 4,5 anos, a biblioteca Java ainda está quebrada, mas devido a sugestão da Vyrx abaixo, finalmente tenho uma solução adequada!

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));
}

A solução é desabilitar a gravação do cabeçalho e escrever o cabeçalho correto antes de serializar o XML no Steam de saída. Coxo, mas produz os resultados corretos. Testes quebrados há mais de 4 anos estão funcionando novamente!

12
AgilePro

Eu tive o mesmo problema no Android ao serializar caracteres emoji. Ao usar a codificação UTF-8 no transformador, a saída era entidades de caracteres HTML (pares substitutos UTF-16), que subsequentemente quebrariam outros analisadores que liam os dados. 

Foi assim que acabei resolvendo:

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"));
1
Vyrx

Para responder a pergunta seguinte, o código funciona para mim. Isso pode levar a codificação de entrada e converter os dados em codificação de saída.

        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);
2
Ramesh Reddy

Passei uma quantidade significativa de tempo depurando esse problema porque ele estava funcionando bem na minha máquina (Ubuntu 14 + Java 1.8.0_45), mas não estava funcionando corretamente na produção (Alpine Linux + Java 1.7).

Ao contrário da minha expectativa, a resposta acima mencionada não ajudou.

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

mas este aqui funcionou como esperado

val out = new StringWriter()
val result = new StreamResult(out)
1
expert

Eu poderia contornar o problema envolvendo o objeto Document passado para o construtor DOMSource. O método getXmlEncoding do meu wrapper sempre retorna null, todos os outros métodos são delegados ao objeto Document encapsulado.

0
Andreas Flueckiger

sobre o quê?:

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
0
Kintan K