it-swarm.dev

transformer.setOutputProperty (OutputKeys.ENCODING, "UTF-8") nefunguje

Mám následující metodu zapsat XMLDom do streamu:

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

Testuji některé další funkce XML a to je jen metoda, kterou používám k zápisu do souboru. Můj testovací program generuje 33 testovacích případů, ve kterých jsou soubory zapisovány. 28 z nich má následující záhlaví:

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

Z nějakého důvodu nyní 1 z testovacích případů produkuje:

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

A další čtyři produkty:

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

Jak můžete jasně vidět, nastavuji ENCODING výstupní klíč na UTF-8. Tyto testy se používají pro práci s dřívější verzí jazyka Java. Neprovádím testy za chvíli (více než rok), ale běžím dnes na "Java (TM) SE Runtime Environment (sestavení 1.6.0_22-b04)" Dostávám toto vtipné chování.

Ověřil jsem, že dokumenty způsobující problém byly přečteny ze souborů, které tyto kódování původně obsahovaly. Zdá se, že nové verze knihoven se pokoušejí zachovat kódování zdrojového souboru, který byl přečten. Ale to není to, co chci ... Opravdu chci, aby výstup byl v UTF-8. 

Ví někdo jiný faktor, který by mohl způsobit, že transformátor bude ignorovat nastavení kódování UTF-8? Je v dokumentu třeba ještě něco, co by mělo zapomenout na kódování původně čteného souboru?

AKTUALIZACE:

Vytáhl jsem stejný projekt na jiném stroji, postavil jsem ho a provedl testy. Na tom stroji projdou všechny testy! Všechny soubory mají v záhlaví "UTF-8". Tento stroj má "Java (TM) SE Runtime Environment (sestavení 1.6.0_29-b11)" Oba počítače jsou spuštěny na Windows 7. Na novém počítači, který pracuje správně, se jdk1.5.0_11 používá k sestavení, ale na starém počítači. Stroj jdk1.6.0_26 se používá k vytvoření sestavy. Knihovny použité pro obě sestavení jsou přesně stejné. Může to být nekompatibilita JDK 1.6 s 1,5 v době budování?

AKTUALIZACE:

Po 4,5 letech je knihovna Java stále rozbitá, ale vzhledem k návrhu ze strany Vyrx níže mám konečně správné řešení!

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

Řešením je zakázat zápis záhlaví a zapsat správnou záhlaví těsně před serializací XML do výstupu Steam. Lame, ale vytváří správné výsledky. Testy rozbité před více než 4 lety jsou nyní opět spuštěny!

12
AgilePro

Měl jsem stejný problém na Android při serializaci znaků emoji. Při použití kódování UTF-8 v transformátoru byl výstup označen znakovými entitami HTML (náhradní páry UTF-16), které následně rozbily další analyzátory, které data načítaly. 

Takto jsem to vyřešil:

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

Pro odpověď na otázku následující kód funguje pro mě. To může mít vstupní kódování a převést data do výstupního kódování.

        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

Strávil jsem značné množství času odladěním tohoto problému, protože to fungovalo dobře na mém počítači (Ubuntu 14 + Java 1.8.0_45), ale ve výrobě nepracovalo správně (Alpine Linux + Java 1.7).

Na rozdíl od mého očekávání z výše uvedené odpovědi nepomohlo.

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

ale tohle fungovalo podle očekávání

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

Tento problém bych mohl vyřešit tak, že objekt Dokument předaný konstruktoru DOMSource zabalím. Metoda getXmlEncoding mého wrapperu vždy vrátí hodnotu null, všechny ostatní metody jsou přeneseny na zabalený objekt dokumentu.

0

co takhle?:

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