2011-01-13 13 views
3

Ich benutze JAXB2, um OXM in einem Spring-WS zu tun. Die angegebene XSD erfordert eine große XML-Datei, die an die Soap-Nachricht angehängt wird. Daher verwende ich MTOM, um die Datei zu übertragen, und habe MTOM auf meinem JAXB2Marshaller aktiviert.JAXB2 Mtom Anhang gebrochen von BOM

Wenn JAXB2 einen MTOM-Anhang mit einem erwarteten MIME-Typ von text/xml als Marshall-Objekt anlegt, wird dieses Element als javax.xml.transform.Source-Objekt bereitgestellt. Nach einiger Suche konnte ich herausfinden, wie ich dieses Source-Objekt an 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(); 
    } 

Das Problem, das ich habe, ist, dass, wenn ich sende UTF-8 kodierten Datei als Befestigungs ich die folgende Fehlermeldung erhalten:

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

Dies wird durch eine Byte Order Mark vor verursacht wird des codierten Textes in der Datei, obwohl diese Stückliste nicht in einer UTF-8-codierten Datei benötigt wird, die nach dem Unicode-Standard zulässig ist, unterstützt Java keine Stücklisten in UTF-8-codierten Streams.

Ich kann dieses Problem lösen, indem ich eine Datei ohne die Stückliste sende, aber das ist nicht wirklich machbar, da es Probleme mit den meisten Microsoft-Produkten verursachen wird, die die Stückliste einfügen.

Es gibt viele Problemumgehungen für die Verweigerung von Sun/Oracle, dieses Problem mit den Streams zu beheben, aber sie erfordern alle Zugriff auf den Stream, das von JAXB2 bereitgestellte Source Object hat keinen InputStream, sondern nur ein Reader-Objekt . Gibt es eine Möglichkeit für mich, dieses Problem zu lösen, indem Sie entweder das Sources Reader-Objekt mit einem Lesegerät umschließen, das eine Stückliste in UTF-8-Codierung ignorieren kann, oder die Art und Weise ändern, in der JAXB2 den Anhang in die Quelle einliest die Stückliste in UTF-8-Codierung.

Vielen Dank im Voraus, Craig

Antwort

3

Der Trick ist, zu "markieren" den Leser. Wenn Ihre Leser nicht Markierung nicht unterstützt können Sie es in einem BufferedReader wickeln, das tut:

OPTION # 1 - Überprüfen Sie für BOM und entfernen Sie

Ich glaube, meine ursprüngliche Code schrieb die Stückliste falsch. Der Quellcode macht weiter unten noch 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 - Suchen '<' und Advance-Reader zu diesem Punkt

lesen, bis Sie die '<' Hit nutzen Zeichen/Reset:

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

} 
+0

Verwenden Sie Dezimal anstelle von Hex für Ihre Char-Konstanten ist * icky *. – dkarp

+0

Danke für den Versuch blaise, leider habe ich das schon ausprobiert und es funktioniert nicht, der Leser, den meine Quelle enthält, implementiert keine Markierung. –

+0

Hier kommt der BufferedReader zum Einsatz. Sie können Ihren Reader mit einem BufferedReader einpacken, um diese Fähigkeit zu erhalten: http://download.oracle.com/javase/6/docs/api/java/io/BufferedReader.html#markSupported%28%29 –