3 Stimmen

JAXB2 Mtom-Anhang durch BOM gebrochen

Ich verwende JAXB2, um OXM in einem Spring-WS durchzuführen. Die XSD, die ich spezifiziert habe, erfordert eine große XML-Datei, die an die Soap-Nachricht angehängt werden muss, also verwende ich MTOM, um die Datei zu übertragen, und habe MTOM auf meinem JAXB2Marshaller aktiviert.

Wenn JAXB2 einen MTOM-Anhang mit dem erwarteten Mime-Type text/xml marshallt, wird dieses Element als javax.xml.transform.Source-Objekt geliefert. Nach einiger Suche konnte ich herausfinden, wie ich dieses Source-Objekt in eine Datei senden kann.

final Source source = request.getSource();
StreamSource streamSource = (StreamSource) source;
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
File file = new File ("/tempxmlfile.xml");
try{
    transformer.transform(streamSource, new StreamResult(file));
    LOG.info("File saved in "+file.getAbsolutePath());
    }
catch(Exception ex){
        ex.getMessage();
    }

Wenn ich eine UTF-8-kodierte Datei als Anhang sende, erhalte ich folgende Fehlermeldung:

[Fatal Error] :1:1: Content is not allowed in prolog.
ERROR:  'Content is not allowed in prolog.'

Dies wird durch ein Byte Order Mark vor dem kodierten Text in der Datei verursacht. Obwohl dieses BOM in einer UTF-8 kodierten Datei nicht erforderlich ist, ist es nach dem Unicode-Standard zulässig, Java unterstützt jedoch keine BOMs in UTF-8 kodierten Streams.

Ich kann dieses Problem lösen, indem ich eine Datei ohne die Stückliste sende, aber das ist nicht wirklich machbar, da es bei den meisten Microsoft-Produkten, die die Stückliste einfügen, zu Problemen führen würde.

Es gibt viele Workarounds für Sun/Oracle's Weigerung, dieses Problem mit den Streams zu beheben, aber sie alle erfordern, dass Sie Zugriff auf den Stream haben, das Source-Objekt, das von JAXB2 bereitgestellt wird, hat keinen InputStream, es hat nur ein Reader-Objekt. Kann ich dieses Problem lösen, indem ich entweder das Reader-Objekt der Quelle mit einem Reader umhülle, der ein BOM in UTF-8-Kodierung ignorieren kann, oder indem ich die Art und Weise ändere, wie JAXB2 den Anhang in die Quelle einliest, so dass es das BOM in UTF-8-Kodierung ignorieren kann?

Vielen Dank im Voraus, Craig

3voto

bdoughan Punkte 144925

Der Trick besteht darin, den Reader zu "markieren". Wenn Ihr Reader die Markierung nicht unterstützt, können Sie ihn in einen BufferedReader einpacken, der dies tut:

OPTION Nr. 1 - Prüfen Sie auf Stücklisten und entfernen Sie sie

Ich glaube, mein ursprünglicher Code hat die Stückliste falsch geschrieben. Der Quellcode unten macht mehr Sinn:

import java.io.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    private static char[] UTF32BE = {0x00, 0x00, 0xFE, 0xFF}; 
    private static char[] UTF32LE = {0xFF, 0xFE, 0x00, 0x00};
    private static char[] UTF16BE = {0xFE, 0xFF}; 
    private static char[] UTF16LE = {0xFF, 0xFE};
    private static char[] UTF8 = {0xEF, 0xBB, 0xBF};

    public static void main(String[] args) throws Exception {
        // Create an XML document with a BOM
        FileOutputStream fos = new FileOutputStream("bom.xml");
        writeBOM(fos, UTF16LE);

        OutputStreamWriter oswUTF8 = new OutputStreamWriter(fos, "UTF-8");
        oswUTF8.write("<root/>");
        oswUTF8.close();

        // Create a Source based on a Reader to simulate source.getRequest()
        StreamSource attachment = new StreamSource(new FileReader(new File("bom.xml")));

        // Wrap reader in BufferedReader so it will support marking
        Reader reader = new BufferedReader(attachment.getReader());

        // Remove the BOM
        removeBOM(reader);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        t.transform(new StreamSource(reader), new StreamResult(System.out));
    }

    private static void writeBOM(OutputStream os, char[] bom) throws Exception {
        for(int x=0; x<bom.length; x++) {
            os.write((byte) bom[x]);
        }
    }

    private static void removeBOM(Reader reader) throws Exception {
        if(removeBOM(reader, UTF32BE)) {
            return;
        }
        if(removeBOM(reader, UTF32LE)) {
            return;
        }
        if(removeBOM(reader, UTF16BE)) {
            return;
        }
        if(removeBOM(reader, UTF16LE)) {
            return;
        }
        if(removeBOM(reader, UTF8)) {
            return;
        }
    }

    private static boolean removeBOM(Reader reader, char[] bom) throws Exception {
        int bomLength = bom.length;
        reader.mark(bomLength);
        char[] possibleBOM = new char[bomLength];
        reader.read(possibleBOM);
        for(int x=0; x<bomLength; x++) {
            if(bom[x] != possibleBOM[x]) {
                reader.reset();
                return false;
            }
        }
        return true;
    }

}

OPTION #2 - Finde '<' und bringe den Leser zu diesem Punkt

Lesen Sie so lange, bis Sie auf die '<'-Hebelmarke/Reset stoßen:

import java.io.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class Demo2 {

    private static char[] UTF32BE = {0x00, 0x00, 0xFE, 0xFF}; 
    private static char[] UTF32LE = {0xFF, 0xFE, 0x00, 0x00};
    private static char[] UTF16BE = {0xFE, 0xFF}; 
    private static char[] UTF16LE = {0xFF, 0xFE};
    private static char[] UTF8 = {0xEF, 0xBB, 0xBF};

    public static void main(String[] args) throws Exception {
        // Create an XML document with a BOM
        FileOutputStream fos = new FileOutputStream("bom.xml");
        writeBOM(fos, UTF16BE);

        OutputStreamWriter oswUTF8 = new OutputStreamWriter(fos, "UTF-8");
        oswUTF8.write("<root/>");
        oswUTF8.close();

        // Create a Source based on a Reader to simulate source.getRequest()
        StreamSource attachment = new StreamSource(new FileReader(new File("bom.xml")));

        // Wrap reader in BufferedReader so it will support marking
        Reader reader = new BufferedReader(attachment.getReader());

        // Remove the BOM
        removeBOM(reader);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        t.transform(new StreamSource(reader), new StreamResult(System.out));
    }

    private static void writeBOM(OutputStream os, char[] bom) throws Exception {
        for(int x=0; x<bom.length; x++) {
            os.write((byte) bom[x]);
        }
    }

    private static Reader removeBOM(Reader reader) throws Exception {
        reader.mark(1);
        char[] potentialStart = new char[1];
        reader.read(potentialStart);
        if('<' == potentialStart[0]) {
            reader.reset();
            return reader;
        } else {
            return removeBOM(reader);
        }
    }

}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X