web-dev-qa-db-fra.com

transformer.setOutputProperty (OutputKeys.ENCODING, "UTF-8") ne fonctionne PAS

J'ai la méthode suivante pour écrire un XMLDom dans un flux:

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

Je teste d'autres fonctionnalités XML, et il ne s'agit que de la méthode que j'utilise pour écrire dans un fichier. Mon programme de test génère 33 cas de test dans lesquels des fichiers sont écrits. 28 d'entre eux ont l'en-tête suivant:

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

Mais pour une raison quelconque, un des cas de test produisent maintenant:

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

Et quatre autres produits:

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

Comme vous pouvez le voir clairement, je règle la clé de sortie ENCODING sur UTF-8. Ces tests fonctionnaient sur une version antérieure de Java. Je n'ai pas exécuté les tests depuis un certain temps (plus d'un an), mais je cours aujourd'hui sur "Java Runtime Environment (build 1.6.0_22-b04)". J'ai ce comportement amusant.

J'ai vérifié que les documents à l'origine du problème avaient été lus à partir de fichiers contenant à l'origine le même encodage. Il semble que les nouvelles versions des bibliothèques tentent de préserver le codage du fichier source lu. Mais ce n'est pas ce que je veux ... Je veux vraiment que la sortie soit en UTF-8. 

Est-ce que quelqu'un connaît un autre facteur qui pourrait amener le transformateur à ignorer le paramètre de codage UTF-8? Y a-t-il autre chose à définir sur le document pour oublier le codage du fichier lu à l'origine?

METTRE À JOUR:

J'ai vérifié le même projet sur une autre machine, construit et ai exécuté les tests là-bas. Sur cette machine, tous les tests sont réussis! Tous les fichiers ont "UTF-8" dans leur en-tête. "Java (SE) Runtime Environment (build 1.6.0_29-b11)", ces deux ordinateurs exécutent Windows 7. Sur la nouvelle machine qui fonctionne correctement, jdk1.5.0_11 est utilisé pour la La machine jdk1.6.0_26 est utilisée pour la construction. Les bibliothèques utilisées pour les deux versions sont exactement les mêmes. Peut-il s'agir d'une incompatibilité JDK 1.6 avec 1.5 au moment de la construction?

METTRE À JOUR:

Après 4,5 ans, la bibliothèque Java est toujours en panne, mais grâce à la suggestion de Vyrx ci-dessous, j'ai enfin une solution adéquate!

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

La solution consiste à désactiver l'écriture de l'en-tête et à écrire l'en-tête correct juste avant de sérialiser le XML sur la sortie Steam. Lame, mais les résultats sont corrects. Les tests passés il y a plus de 4 ans sont maintenant en cours!

12
AgilePro

J'ai eu le même problème sur Android lors de la sérialisation des caractères emoji. Lors de l'utilisation du codage UTF-8 dans le transformateur, les sorties étaient des entités de caractères HTML (paires de substitution UTF-16), qui casseraient par la suite les autres analyseurs qui lisent les données. 

Voici comment j'ai fini par le résoudre:

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

Pour répondre à la question, le code suivant fonctionne pour moi. Cela peut prendre le codage d'entrée et convertir les données en codage de sortie.

        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

J'ai passé beaucoup de temps à résoudre ce problème car il fonctionnait bien sur ma machine (Ubuntu 14 + Java 1.8.0_45) mais ne fonctionnait pas correctement en production (Alpine Linux + Java 1.7).

Contrairement à mes attentes, la réponse ci-dessus ne m'a pas aidé.

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

mais celui-ci a fonctionné comme prévu

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

Je pourrais contourner le problème en encapsulant l'objet Document transmis au constructeur DOMSource. La méthode getXmlEncoding de mon wrapper renvoie toujours la valeur null, toutes les autres méthodes sont déléguées à l'objet Document enveloppé.

0
Andreas Flueckiger

qu'en est-il de?:

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