Search code examples
javaxmljaxbunmarshallingxop

Unmarshalling XOP with JAXB


I have a data source that has switched from base64 embedded image data to XOP image data. I'm using Java/JAXB to unmarshal the data and can't find any good sources describing how it's done. All references seem to describe doing this with SOAP messages which seem to take care of some of the heavy lifting for you.

In my case, the data is coming in effectively as a string that needs to be unmarshalled into objects created by JAXB. Since the new messages start with

Mime-Version: 1.0
Content-Type: multipart/related; start-info="text/xml"; type="application/xop+xml"; 
    start="<[email protected]>"; 
    boundary="----=_Part_0_-338193320.1400077877317"

obviously it won't unmarshal the data as XML as it doesn't look like XML (no data allowed before prolog).

Can this be done with JAXB? The creation of the object from the schema seems to work fine and creates what looks like a proper object (it creates an Include element). I've tried creating an AttachmentUnmarshaller manually but it hasn't helped ... the incoming data is still not recognized as XML. I feel like I'm missing a fundamental step but can't find any good references or examples.

Any help would be greatly appreciated.


Solution

  • Below is an approach that might help.

    Message

    Below is roughly what you are going to be processing:

     MIME-Version: 1.0
     Content-Type: Multipart/Related;boundary=MIME_boundary;
     ...
     --MIME_boundary
     Content-Type: application/xop+xml; 
     ...
    
     <soap:Envelope ...
      <soap:Body>...
        <foo>
          <photo xmlmime:contentType='image/png'>
            <xop:Include xmlns:xop='http://www.w3.org/2004/08/xop/include' 
               href='cid:http://example.org/me.jpeg'/></m:photo>
     ...
    
     --MIME_boundary
     Content-Type: image/png
     Content-Transfer-Encoding: binary
     Content-ID: <http://example.org/me.png>
    
     // binary octets for png
    

    Demo Code

    Demo

    The code below assumes you have processed the message to do the following:

    1. Extract the XML fragment
    2. Extract the attachments and are able to key them on the cid.
    import java.io.File;
    import javax.xml.bind.*;
    
    public class Demo {
    
        private static String base64 = "/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5/ooooA//2Q==";
    
        public static void main(String[] args) throws Exception {
            // Create the JAXBContext & Unmarshaller
            JAXBContext jc = JAXBContext.newInstance(Foo.class);
            Unmarshaller unmarshaller = jc.createUnmarshaller();
    
            // Create a custom AttachementUnmarshaller.  Populate the Map
            // AttachmentUnmarshaller with the attachments keyed on the cid.
            MyAttachmentUnmarshaller attachmentUnmarshaller = new MyAttachmentUnmarshaller();
            attachmentUnmarshaller.getAttachments().put("cid:http://example.org/me.jpeg", DatatypeConverter.parseBase64Binary(base64));
    
            // Set the AttachmentUnmarshaller on the Unmarshaller
            unmarshaller.setAttachmentUnmarshaller(attachmentUnmarshaller);
    
            // Unmarshal the XML piece
            File xml = new File("src/forum24407360/input.xml");
            Foo foo = (Foo) unmarshaller.unmarshal(xml);
        }
    
    }
    

    MyAttachmentUnmarshaller

    Below is what the AttachmentUnmarshaller looks like. An AttachmentUnmarshaller is passed in the cid and is responsible for returning the corresponding binary data. In a JAX-WS environment this is handled automatically for you, but there is nothing preventing you from doing it manually. You can find more information about it here: What's the most standard Java way to store raw binary data along with XML?.

    import java.io.*;
    import java.util.*;
    import javax.activation.*;
    import javax.xml.bind.attachment.AttachmentUnmarshaller;
    
    public class MyAttachmentUnmarshaller extends AttachmentUnmarshaller {
    
        private Map<String, byte[]> attachments = new HashMap<String, byte[]>();
    
        public Map<String, byte[]> getAttachments() {
            return attachments;
        }
    
        @Override
        public DataHandler getAttachmentAsDataHandler(String cid) {
            byte[] bytes = attachments.get(cid);
            return new DataHandler(new ByteArrayDataSource(bytes));
        }
    
        @Override
        public byte[] getAttachmentAsByteArray(String cid) {
            return attachments.get(cid);
        }
    
        @Override
        public boolean isXOPPackage() {
            return true;
        }
    
        private static class ByteArrayDataSource implements DataSource {
    
            private byte[] bytes;
    
            public ByteArrayDataSource(byte[] bytes) {
                this.bytes = bytes;
            }
    
            public String getContentType() {
                return  "application/octet-stream";
            }
    
            public InputStream getInputStream() throws IOException {
                return new ByteArrayInputStream(bytes);
            }
    
            public String getName() {
                return null;
            }
    
            public OutputStream getOutputStream() throws IOException {
                return null;
            }
    
        }
    
    }
    

    XML Piece

    <foo>
        <image>
            <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:http://example.org/me.jpeg"/>
        </image>
    </foo>