XML encoding wird nicht übernommen

Bitte aktiviere JavaScript!
Hallo zusammen,

ich möchte eine XML datei erzeugen, welche auf einen bestimmten Art codiert wird, und zwar "ISO-8859-15".

Nun wenn ich einen Writer erzeuge um diese in String umwandel, dann kriege ich UTF-8 kodierung.

ein Code ist:

Code:
Art art= objectFactory.createElster();
        art.setTransferHeader(getTransferHeader(fachContext, land));
        art.setDatenTeil(getDatenTeil(rootElement, versionDerAnwendung));

        try {
            JAXBContext jc = JAXBContext.newInstance(art.class, kapestClass);
            Marshaller m = jc.createMarshaller();
            m.setProperty("jaxb.encoding", VerarbeitungService.OUTPUT_ENCODING);

            Writer writer = new StringWriter();
            m.marshal(art, NoNamespacesWriter.filter(writer));  // -> Namespaces komplett entfernen.

            result = writer.toString();
            result = result.replace("<Art>", "<Art xmlns=\"http://www.art.de/2002/XMLSchema\">");

        } catch (Exception exception) {
            throw new KapestException(TechnischeFehler.FCT0032, exception);
        }

        return result;
Code:
public interface VerarbeitungService {

    public static final String OUTPUT_ENCODING = "ISO-8859-15";
}
result soll dann die XML sein.
m setzt die encoding="ISO-8859-15"

aber der writer hat UTF-8 und UTF-8 steht dann am Ende in der XML.

Kann mir einer sagen wieso es so ist nd wie ich das beheben kann.
Ich kann natürlich auch ganz einfach am ende vor return ein replace machen, aber ich denke das ist keine gute Lösung.

Danke
 
A

Anzeige




Vielleicht hilft dir unser Java-Tutorial hier weiter —> (hier klicken)
Also Du erstellst ein String und der String ist in Java immer gleich codiert. Intern ist der String ein UTF-16 (Siehe https://docs.oracle.com/javase/7/docs/api/java/lang/String.html : "A String represents a string in the UTF-16 format".

Also egal, wie Du den String erzeugt hast: Daraus wird dann ein UTF-16 intern und je nachdem, wie Du damit weiter vorgehst, wird der String dann entsprechend ausgegeben.

String hat ein getBytes(encoding). Damit bekommst Du Bytes, die den String in einer anderen codierung representieren.
 
@kneitzel Ich bin mir nicht so ganz sicher, ob du mein Problem verstanden hast :)..

Es geht um encoding in der XML datei:
<?xml version='1.0' encoding='UTF-8'?>

eigentlich sollte ich mit der Zeile :
Code:
m.setProperty("jaxb.encoding", VerarbeitungService.OUTPUT_ENCODING);
encoding ändern zu:
<?xml version='1.0' encoding='ISO-8859-15'?>

das passiert auch wenn ich debuggen, aber sobald ich "wirter" benutze und mit writer.toString() den "result" setze dann habe ich wieder UTF-8.
 
Edit: UPS. Sorry.

Also das Problem ist, dass auch in der Datei im Header das falsche Encoding steht und nicht das falsche Encoding in einer Datei zu finden ist oder so. Sorry. Hab dann nicht gut genug gelesen!
 
Also nächster Versuch :)

Wenn Du marshal mit einem Writer aufrufst, dann muss der Writer mit dem richtigen Encoding erstellt werden. Ein Beispiel dafür wäre (von http://blog.bdoughan.com/2011/08/jaxb-and-java-io-files-streams-readers.html übernommen):
Java:
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");
OutputStreamWriter writer = new OutputStreamWriter(System.out, "ISO-8859-1");
marshaller.marshal(address, writer);
Aber die ganz exakte Begründung, wie er da genau bei einem Writer das Encoding prüft, kann ich gerade auch nicht sagen, aber das wäre evtl. ein Versuch, dass man dies etwas umstellt.
 
Hmm, also ich habe jetzt noch ein paar Tests gemacht weil ich das gefundene so nicht wirklich nachvollziehen konnte.

Also als erstes noch einmal zum Verständnis: Dein Problem ist, dass die Ausgabe am Anfang als Encoding eben nicht sowas wie
<?xml version="1.0" encoding="ISO-8859-15" standalone="yes"?>
aufzeigt sondern statt ISO-8859-15 ein UTF-16 oder so?

Es geht also nicht um das Encoding, welches Du in den Bytes siehst sondern um den XML Header!

Ich habe ein kleinen Test aufgebaut, denn die Aussage, dass der Writer richtig sein muss, konnte ich nicht nachvollziehen, denn Writer bietet keine Möglichkeit, das Encoding abzufragen...

Und tatsächlich: Die Ausgabe erfolgte immer korrekt.

Mein minimales Beispiel ist eine einfache Klasse Customer:

Java:
package xmltest;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    String name;
    int age;
    int id;

    public String getName() {
        return name;
    }

    @XmlElement
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    @XmlElement
    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

    @XmlAttribute
    public void setId(int id) {
        this.id = id;
    }

}
Und dann der eigentliche Test, in dem ich mehrere Ausgaben mache (Datei, System.out und StringWriter):
Java:
package xmltest;

import java.io.File;
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class JAXBExample {
    public static void main(String[] args) {

        Customer customer = new Customer();
        customer.setId(100);
        customer.setName("konrad");
        customer.setAge(47);

        try {

            File file = new File("./file.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

            // output pretty printed
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-15");
            jaxbMarshaller.marshal(customer, file);
            jaxbMarshaller.marshal(customer, System.out);

            Writer writer = new StringWriter();
            jaxbMarshaller.marshal(customer, writer);
            System.out.println(writer.toString());
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}
Und wie erwartet: Es wurde immer das gewünschte encoding="ISO-8859-15" ausgegeben.

Was mir hier aber fehlt und was ich nicht kenne: Was ist denn Deine Klasse NoNamespacesWriter? Kannst Du die Implementation in der Klasse einmal aufzeigen?

Und sorry, wenn es Verständnisprobleme gibt/gab und ich teilweise selbst etwas am raten war. So ein lauffähiges Beispiel ist bei sowas immer gut und wichtig.
 
statt ISO-8859-15 ein UTF-16 oder so?
UTF-8 :)

Was ist denn Deine Klasse NoNamespacesWriter? Kannst Du die Implementation in der Klasse einmal aufzeigen?
Ja , so ist die Klasse implemntiert:
Code:
public class NoNamespacesWriter extends DelegatingXMLStreamWriter {
    
    private static final NamespaceContext emptyNamespaceContext = new NamespaceContext() {

        @Override
        public String getNamespaceURI(String prefix) {
          return "";
        }

        @Override
        public String getPrefix(String namespaceURI) {
          return "";
        }

        @SuppressWarnings("rawtypes")
        @Override
        public Iterator getPrefixes(String namespaceURI) {
          return null;
        }

      };

      public static XMLStreamWriter filter(Writer writer) throws XMLStreamException {
        return new NoNamespacesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer));
      }
      
      public NoNamespacesWriter(XMLStreamWriter writer) {
        super(writer);
      }

      @Override
      public NamespaceContext getNamespaceContext() {
        return emptyNamespaceContext;
      }
}

Und wie erwartet: Es wurde immer das gewünschte encoding="ISO-8859-15" ausgegeben.
Ja bei mir aber leider nicht. Ich bekomme immer UTF-8 ausgegeben.
 
Ich hab die zeile:
Code:
 m.marshal(art, NoNamespacesWriter.filter(writer));
durch
Code:
m.marshal(art, writer);
ersetzt.
und das funktioniert.

Ich vermute XMLStreamWriter setzt encoding wieder zurück auf UTF-8. oder ?
 
Wobei XMLStreamWriter ja nur ein Interface vorgibt. Da ist aber teilweise klar definiert: writeStartDocument() setzt als Encoding UTF-8 und Version 1.0. Aber ich würde jetzt vom Marshaller erwarten, dass er das writeStartDocument(encoding, version) nutzt, welche dann auch das richtige Encoding schreiben sollte.

Ich kenne jetzt aber auch den DelegatingXMLStreamWriter nicht und auch die exakte Implementation vom Marshaler. Daher ist schwer zu sagen, an welcher Stelle das Problem auftritt. Evtl. kannst Du mal probieren, in NoNamespaceWriter die writeStartDocument Funktionen zu überschreiben. Du könntest zum einen loggen, was aufgerufen wird (Dann sehen wir ja, welche writeStartDocument Funktion benutzt wird) und sollte der Aufruf unstimmig sein, dann könnte man writeStartDocument mit richtigem Encoding aufrufen.
 
hab mein Code wie folgt geändert:
Code:
    try {
            JAXBContext jc = JAXBContext.newInstance(art.class, kapestClass);
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            m.setProperty(Marshaller.JAXB_ENCODING, VerarbeitungService.OUTPUT_ENCODING);
            Writer writer = new StringWriter();
            XMLStreamWriter xmlStreamWriter = NoNamespacesWriter.filter(writer);
            xmlStreamWriter.writeStartDocument(VerarbeitungService.OUTPUT_ENCODING, "1.0");
            m.marshal(art, xmlStreamWriter);
            result = xmlStreamWriter.toString();

        } catch (Exception exception) {
            throw new KapestException(TechnischeFehler.FCT0032, exception);
        }

        return result;
wobei bei der zeile m.marshal(art, xmlStreamWriter); fliegt eine excaption
 
Ja, das hätte ich auch vermutet, denn der Aufruf will ja das StartDocument auch schreiben und wenn es schon drin steht, dann hätte ich vom XMLStreamWriter genau so etwas erwartet.

Meine Idee, das einmal etwas auszutesten habe ich einmal selbst umgesetzt. Dazu habe ich in gradle als dependency eingetragen:
Code:
compile group: 'org.apache.cxf', name: 'cxf-core', version: "3.3.1"
Das dürfte in etwa Deine Abhängigkeit sein.

Dann Deine Klasse etwas erweitert durch die Overrides:
Java:
package xmltest;

import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;

import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.Writer;
import java.util.Iterator;

public class NoNamespacesWriter extends DelegatingXMLStreamWriter {

    private static final NamespaceContext emptyNamespaceContext = new NamespaceContext() {

        @Override
        public String getNamespaceURI(String prefix) {
            return "";
        }

        @Override
        public String getPrefix(String namespaceURI) {
            return "";
        }

        @SuppressWarnings("rawtypes")
        @Override
        public Iterator getPrefixes(String namespaceURI) {
            return null;
        }

    };

    public static XMLStreamWriter filter(Writer writer) throws XMLStreamException {
        return new NoNamespacesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer));
    }

    public NoNamespacesWriter(XMLStreamWriter writer) {
        super(writer);
    }

    @Override
    public NamespaceContext getNamespaceContext() {
        return emptyNamespaceContext;
    }

    @Override
    public void writeStartDocument() throws XMLStreamException {
        System.out.println("writeStartDocument()");
        super.writeStartDocument("ISO-8859-15", "1.0");
    }

    @Override
    public void writeStartDocument(String encoding, String ver) throws XMLStreamException {
        System.out.println("writeStartDocument(" + encoding + ", " + ver + ")");
        super.writeStartDocument(encoding, ver);
    }

    @Override
    public void writeStartDocument(String ver) throws XMLStreamException {
        System.out.println("writeStartDocument(" + ver + ")");
        super.writeStartDocument(ver);
    }
}
Und natürlich dann auch aufgerufen:
Java:
package xmltest;

import java.io.File;
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class JAXBExample {
    public static void main(String[] args) {

        Customer customer = new Customer();
        customer.setId(100);
        customer.setName("konrad");
        customer.setAge(47);

        try {

            File file = new File("./file.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

            // output pretty printed
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-15");
            jaxbMarshaller.marshal(customer, file);
            jaxbMarshaller.marshal(customer, System.out);

            Writer writer = new StringWriter();
            jaxbMarshaller.marshal(customer, NoNamespacesWriter.filter(writer));
            System.out.println(writer.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Das Ergebnis ist tatsächlich, dass der Marshaler writeStartDocument() aufruft. Meine Erwartungshaltung hier wäre, dass eben writeStartDocument(encoding, version) aufgerufen wird, da ja ein Encoding gesetzt wurde. (Bzw. immer, da ja ein Encoding vorliegt.)

Als Workaround kann man das überschreiben. Die ganzen System.out.println gehen dann natürlich und meine Version, die ich vorschlagen würde, poste ich gleich noch. Ich passe es eben schnell selbst an, anstatt das nur zu beschreiben.
 
Also mein Vorschlag wäre jetzt, die Klasse NoNamespaceWriter zu erweitern um encoding und version:
Java:
package xmltest;

import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;

import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.Writer;
import java.util.Iterator;

public class NoNamespacesWriter extends DelegatingXMLStreamWriter {

    /**
     * Encoding that should be used. Defaults to UTF-8
     */
    private String encoding = "UTF-8";
    public String getEncoding() { return encoding; }
    public void setEncoding(String encoding) { this.encoding = encoding; }

    /**
     * Version that should be used. Defaults to 1.0
     */
    private String version = "1.0";
    public String getVersion() { return version; }
    public void setVersion(String version) { this.version = version; }

    private static final NamespaceContext emptyNamespaceContext = new NamespaceContext() {

        @Override
        public String getNamespaceURI(String prefix) {
            return "";
        }

        @Override
        public String getPrefix(String namespaceURI) {
            return "";
        }

        @SuppressWarnings("rawtypes")
        @Override
        public Iterator getPrefixes(String namespaceURI) {
            return null;
        }

    };

    public static XMLStreamWriter filter(Writer writer) throws XMLStreamException {
        return new NoNamespacesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer));
    }

    public static XMLStreamWriter filter(Writer writer, String encoding) throws XMLStreamException {
        return new NoNamespacesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer), encoding);
    }

    public static XMLStreamWriter filter(Writer writer, String encoding, String version) throws XMLStreamException {
        return new NoNamespacesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer), encoding, version);
    }

    public NoNamespacesWriter(XMLStreamWriter writer) {
        super(writer);
    }

    public NoNamespacesWriter(XMLStreamWriter writer, String encoding) {
        super(writer);
        this.encoding = encoding;
    }

    public NoNamespacesWriter(XMLStreamWriter writer, String encoding, String version) {
        super(writer);
        this.encoding = encoding;
        this.version = version;
    }

    @Override
    public NamespaceContext getNamespaceContext() {
        return emptyNamespaceContext;
    }

    @Override
    public void writeStartDocument() throws XMLStreamException {
        super.writeStartDocument(encoding, version);
    }

    @Override
    public void writeStartDocument(String encoding, String ver) throws XMLStreamException {
        super.writeStartDocument(encoding, ver);
    }

    @Override
    public void writeStartDocument(String ver) throws XMLStreamException {
        super.writeStartDocument(encoding, ver);
    }
}
Und die Nutzung gibt dann das Encoding erneut an:
Java:
package xmltest;

import java.io.File;
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class JAXBExample {
    public static void main(String[] args) {

        Customer customer = new Customer();
        customer.setId(100);
        customer.setName("konrad");
        customer.setAge(47);

        try {

            File file = new File("./file.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

            // output pretty printed
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-15");
            jaxbMarshaller.marshal(customer, file);
            jaxbMarshaller.marshal(customer, System.out);

            Writer writer = new StringWriter();
            jaxbMarshaller.marshal(customer, NoNamespacesWriter.filter(writer, "ISO-8859-15"));
            System.out.println(writer.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
So ganz 100% gefällt mir dies nicht, da das Encoding eigentlich unnötig erneut angegeben werden muss. Ist halt nur ein Workaround.
 
Ist genau nach Doku implementiert, wenn ich die richtig lese:

Afaik muss das Encoding auch noch für den Writer passend gesetzt werden, je nachdem wohin man schreibt.
Also das ist in so fern richtig, als das man ja die Ausgabe im richtigen Format haben will. Was nützt es, wenn im Header etwas anderes steht als die Datei wirklich codiert ist. Das war ja, was ich zuerst vermutet hatte: Er bekommt den Text im String und dann schreibt es den String irgendwohin und dann wird es halt UTF-16 codiert.

Aber hier liegt tatsächlich ein Fehler vor aus meiner Sicht. Dem Marshaller wird ja klar gesagt, dass er ein bestimmtes encoding nutzen soll. Aber bei dem XmlStreamWriter Overlay wird ganz offensichtlich genau dies missachtet. Es wird generell die writeStartDocument Funktion aufgerufen, die UTF-8 nutzt, unabhängig davon, ob ein Encoding gesetzt wurde. Daher wurde das dann in der Implementation von mir entsprechend gesetzt im Test:
Java:
    @Override
    public void writeStartDocument() throws XMLStreamException {
        System.out.println("writeStartDocument()");
        super.writeStartDocument("ISO-8859-15", "1.0");
    }
(Dann später natürlich mit eigener Property, die auch im Konstruktor gesetzt werden kann!)
 
Wo bitte ist es genau so, wie in der Doku beschrieben? Das, was Du kopiert hast, besagt doch, dass es eben mit der setProperty API gehen soll. Genau die wurde von Anfang an genutzt. Und die funktioniert für die meisten marshal overlays. Nur eben bei der XmlStreamWriter Variante kommt etwas anderes heraus.

Spätestens das sollte doch als Fehler angesehen werden. Wenn mehrere Versionen der marshal Funktion existieren die je nach gewünschter Ausgabe eben ein Stream, File, Result, was weiss ich entgegen nehmen, dann ist es unlogisch, dass bei n-1 Versionen das Encoding genommen wird und bei einer eben immer UTF-8 genommen wird. Und das nicht aus zwingenden Gründen, sondern nur, weil der Entwickler der Meinung war, writeStartDocument() statt writeStartDocument(encoding, version) zu verwenden.

Daher ist zumindest der Abschnitt der Doku, den Du gebracht hast, keine Erklärung des Verhaltens. (Davon mal abgesehen, dass es ehh unsinnig ist, da ja schlicht ein anderer Overload einer Funktion benutzt werden muss! Also selbst, wenn in der Doku stehen würde, dass die Marshal Funktion bei dem overload die Property Encoding ignoriert, wäre das schlicht Unsinn.)
 
Encoding

By default, the Marshaller will use UTF-8 encoding when generating XML data to a java.io_OutputStream, or a java.io.Writer. Use the setProperty API to change the output encoding used during these marshal operations.
XmlStreamWriter ist weder OutputStream noch Writer, von daher sollte bei strenger Auslegung der Doku mMn das Encoding dabei nicht greifen.

Ob das sinnvoll ist, ist eine andere Frage, vermutlich gibts aber irgendeinen historischen Grund dafür.
 
A

Anzeige




Hier lernst du alle wichtigen Java-Grundlagen.
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben