2013-03-23 15 views
11

Mam następujący sposób napisania XMLDom do strumienia:transformer.setOutputProperty (OutputKeys.ENCODING, "UTF-8") nie działa

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

jestem testowania jakieś inne funkcjonalności XML, a to jest po prostu metodą, której używam do zapisu w pliku. Mój program testowy generuje 33 przypadki testowe, w których zapisywane są pliki. 28 z nich mają następujący nagłówek:

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

Ale z jakiegoś powodu, 1 przypadków testowych teraz produkować:

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

i cztery więcej produkujemy:

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

jak można wyraźnie widzisz, ustawiam klucz wyjściowy ENCODING na UTF-8. Testy te działały na wcześniejszej wersji Java. Nie uruchomiłem testów od jakiegoś czasu (ponad rok), ale dzisiaj działam w środowisku Java Runtime Environment (build 1.6.0_22-b04) "Dostaję to zabawne zachowanie.

Sprawdziłem, czy dokumenty powodujące problem zostały odczytane z plików, które pierwotnie miały te kodowanie. Wygląda na to, że nowe wersje bibliotek próbują zachować kodowanie odczytanego pliku źródłowego. Ale to nie jest to, czego chcę ... Naprawdę chcę, żeby wyjście było w UTF-8.

Czy ktoś wie o jakimkolwiek innym czynniku, który może spowodować, że transformator zignoruje ustawienie kodowania UTF-8? Czy jest coś jeszcze, co należy ustawić w dokumencie, aby zapomnieć o kodowaniu pliku, który został pierwotnie odczytany?

UPDATE:

I wyrejestrowany tego samego projektu na innym komputerze, zbudowany i prowadził tam badania. Na tym komputerze wszystkie testy mijają! Wszystkie pliki mają "UTF-8" w nagłówku. Ta maszyna ma "Java (TM) SE Runtime Environment (kompilacja 1.6.0_29-b11)" Na obu komputerach działa Windows 7. Na nowej maszynie, która działa poprawnie, jdk1.5.0_11 służy do budowania, ale na starym maszyna jdk1.6.0_26 służy do tworzenia kompilacji. Biblioteki używane w obu wersjach są dokładnie takie same. Czy może to być niezgodność JDK 1.6 z 1.5 w czasie kompilacji?

UPDATE:

Po 4,5 roku, biblioteki Java jest nadal uszkodzony, ale z powodu sugestii Vyrx poniżej, w końcu ma właściwe rozwiązanie!

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

Rozwiązaniem jest wyłączenie pisanie nagłówka i napisać poprawny nagłówek tuż przed szeregowania XML do pary wyjściowej. Lame, ale daje prawidłowe wyniki. Testy zerwane ponad 4 lata temu są teraz uruchomione ponownie!

+1

To rzeczywiście wygląda jak jakiś błąd lub niezgodności problemu. Jest mało prawdopodobne, aby ktokolwiek mógł pomóc bez powtarzalnej wersji testowej. Czy możesz podać [SSCCE] (http://sscce.org/) i wymienić wszystkie wersje narzędzi/bibliotek? – sleske

+0

Istnieje kilka miejsc, w których należy sprawdzić lokalizację. Twój lokalny komputer ma ustawienia regionalne, twój IDE może mieć ustawienia regionalne, a proces JVM ma ustawienia regionalne. Widziałem już takie problemy, zanim zmieniły się moje ustawienia regionalne. Jak prowadzisz testy? java.exe, maven, IDE? –

+0

Ponieważ określiłem kodowanie UTF-8 bezpośrednio, ustawienia regionalne nie powinny mieć znaczenia, ale aby bezpośrednio odpowiedzieć na twoje pytanie, kod testowy jest wywoływany jako wywołanie linii komend do Java.exe, w systemie Windows, zlokalizowanym na wybrzeżu Pacyfiku w USA. i skonfigurowane dla strefy czasowej w USA i Pacyfiku. – AgilePro

Odpowiedz

1

Miałem ten sam problem na Androidzie podczas serializacji znaków emoji. Podczas korzystania z kodowania UTF-8 w transformatorze, wyjściem były jednostki znaków HTML (pary zastępcze UTF-16), które następnie łamały inne parsery odczytujące dane.

ten sposób skończyło się go rozwiązać:

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")); 
+0

Tak, to wygląda na to, że działa. NIE jestem fanem konwersji całego drzewa XML na ciąg znaków w pamięci (szczególnie, że StringWriter nie jest w tym wydajny). Naprawdę nalegam na przesyłanie strumieniowe bezpośrednio na wyjście. Możliwym rozwiązaniem jest zamiast dodawania nagłówka po serializacji, zapisanie nagłówka do strumienia wyjściowego PRZED serializacją XML bez nagłówka do tego samego strumienia wyjściowego. Zobaczę, czy to działa. – AgilePro

+1

Przepisałem ten pomysł tak, by właściwie wykorzystywał strumienie i dawał ci kredyt za odpowiedź. (dzięki!) Jak to napisałeś, będziesz miał trzy kopie dokumentu w pamięci w tym samym czasie. Dla małych XML nie jest problemem, ale generalnie posiadanie trzech kopii ważnego pliku danych w pamięci nie jest wydajne. Lepszym rozwiązaniem jest po prostu napisanie nagłówka przed serializowaniem XML do pisarza. Przeredagowałem twoją odpowiedź, aby zrobić tylko 2 kopie XML w pamięci. – AgilePro

-1

Robię tu dzikie ujęcie, ale wspominasz, że czytasz pliki z danymi z testów. Czy możesz upewnić się, że czytasz pliki za pomocą właściwego kodowania, więc podczas pisania do OutputStream masz już dane w odpowiednim kodowaniu?

A więc coś w rodzaju nowego InputStreamReadera (nowy FileInputStream (fileDir), "UTF8").

nie należy zapominać, że konstruktorzy pojedynczym argumentem FileReader zawsze używać domyślnej platformy kodowania: The constructors of this class assume that the default character encoding and the default byte-buffer size are appropriate.

+0

Nigdy nie używam FileReadera. --- DOM "Dokument" używa wartości ciągu znaków, co oznacza, że ​​zostały już przekonwertowane z ich pierwotnej postaci. Korzystam z programów narzędziowych Java DOM, aby odczytać plik bezpośrednio ze strumienia bajtów. Oczekuje się, że strumień zostanie zinterpretowany zgodnie z nagłówkiem XML, który określa kodowanie. Tak działa XML. --- Wydaje się, że plik został poprawnie odczytany i jest zapisany w określonym kodowaniu - po prostu nie jest to kodowanie, o które prosiłem, aby go zapisać. – AgilePro

2

odpowiedzieć na pytanie Następujący kod działa dla mnie. Może to wymagać kodowania wejściowego i konwersji danych na kodowanie wyjściowe.

 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); 
+0

Błąd występuje tylko w niektórych wersjach Java.Nie miałem czasu, aby przeprowadzić pełne badanie dokładnie tego, jakie środowisko powoduje problem, ani nawet czasu, aby opublikować kod testowy tutaj, jednak jest on zasadniczo podobny do tego, co publikujesz. Niepowodzenia były automatyczne testy, które trwały przez lata. Kod, który zawierasz, jest dobrym przykładem tego, jak przetestować problem. Nie wiem, czy będę w stanie powrócić do pierwotnego środowiska, które uległo awarii i ponownie uruchomić testy tam. Wszystko, w pełni czasu ... – AgilePro

-1

Spróbuj ustawić kodowanie na StreamResult szczególności:

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

ten sposób powinno być tylko w stanie napisać w UTF-8.

+2

Problem polega na tym, że "nagłówek" jest niepoprawny. Jeśli w nagłówku jest napisane, że jest to ISO-8859-1, to nie chciałbym, aby był faktycznie zakodowany w inny sposób. Potrzebuję zarówno nagłówka, jak i rzeczywistego kodowania strumienia. Dlatego w tych bibliotekach zawsze używam strumieni wejścia/wyjścia, a nie czytnika/pisarza ... ponieważ standard mówi, że musisz przeczytać nagłówek, aby dowiedzieć się, jakie jest kodowanie. – AgilePro

0

co ?:

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 
1

spędziłem znaczną ilość czasu debugowania tego problemu, ponieważ działa dobrze na moim komputerze (Ubuntu 14 + Java 1.8.0_45), ale nie działa prawidłowo produkcja (Alpine Linux + Java 1.7).

Wbrew moim oczekiwaniom, po wyżej wspomnianej odpowiedzi, nie pomogło.

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

ale ten pracował jako oczekiwanego

val out = new StringWriter() 
val result = new StreamResult(out) 
0

mogę obejść ten problem poprzez owinięcie przekazany do konstruktora DOMSource obiektu dokumentu. Metoda getXmlEncoding mojego opakowania zawsze zwraca wartość null, wszystkie inne metody są delegowane do owiniętego obiektu Document.