java - Un/marshalling base64 encoded binary data as stream -


i have application importing , exporting data oracle database to/from xml using jaxb. there blob fields in db containing uploaded files have in xml base64 encoded string. works quite out of box jaxb using @xmlschematype(name = "base64binary") done below:

@xmltype public class documenttemplatefile {      // other fields ommited      @xmlelement(required = true)     @xmlschematype(name = "base64binary")     private byte[] data;      // other code ommited } 

the problem solution whole file content stored in memory because of byte array. depending on size of file cause issues.

i therefore wondering if there's way create xmladapter or similar gets streams , file, can stream directly / db's blob without having whole content in memory. thinking on similar this:

public class blobxmladapter extends xmladapter<inputstream, outputstream> {    @override   public inputstream marshal(final outputstream value) throws exception {     return null;   }    @override   public outputstream unmarshal(final inputstream value) throws exception {     return null;   }  } 

this illustrative example can have idea of i'm looking for. end solution doesn't have make use of xmladaters. need way hook on un/marshalling process , stream data through buffer / queue rather storing in byte array.

this solution uses following third party library. should use following maven dependency:

<dependency>     <groupid>jlibs</groupid>     <artifactid>jlibs-xsd</artifactid>     <version>2.0</version> </dependency>  <repository>     <id>jlibs-snapshots-repository</id>     <name>jlibs snapshots repository</name>     <url>https://raw.githubusercontent.com/santhosh-tekuri/maven-repository/master</url>     <layout>default</layout> </repository> 

we need use following custom xmladapter:

import javax.xml.bind.annotation.adapters.xmladapter; import java.io.file;  /**  * @author santhosh kumar tekuri  */ public class base64adapter extends xmladapter<string, file>{     @override     public file unmarshal(string v) throws exception{         return new file(v);     }      @override     public string marshal(file v) throws exception{         throw new unsupportedoperationexception();     } } 

now change pojo use above adapter:

import javax.xml.bind.annotation.xmlelement; import javax.xml.bind.annotation.xmlrootelement; import javax.xml.bind.annotation.xmlschematype; import javax.xml.bind.annotation.adapters.xmljavatypeadapter; import java.io.file;  @xmlrootelement public class documenttemplatefile {     @xmlelement(required = true)     public string username;      @xmlelement(required = true)     @xmlschematype(name = "base64binary")     @xmljavatypeadapter(base64adapter.class)     public file data; } 

now following helper class should used read xml file:

import jlibs.xml.namespaces; import jlibs.xml.xsd.domlsinputlist; import jlibs.xml.xsd.xsparser; import jlibs.xml.xsd.xsutil; import org.apache.xerces.xs.xselementdeclaration; import org.apache.xerces.xs.xsmodel; import org.apache.xerces.xs.xssimpletypedefinition; import org.apache.xerces.xs.xstypedefinition; import org.xml.sax.attributes; import org.xml.sax.inputsource; import org.xml.sax.saxexception; import org.xml.sax.xmlreader; import org.xml.sax.helpers.xmlfilterimpl;  import javax.xml.bind.jaxbcontext; import javax.xml.bind.schemaoutputresolver; import javax.xml.namespace.qname; import javax.xml.parsers.saxparserfactory; import javax.xml.transform.result; import javax.xml.transform.sax.saxsource; import javax.xml.transform.stream.streamresult; import java.io.*; import java.util.arraylist; import java.util.hashmap; import java.util.list; import java.util.map;  /**  * @author santhosh kumar tekuri  */ public class jaxbblobutil{     public static xsmodel generateschemas(class clazz) throws exception{         final map<string, bytearrayoutputstream> schemas = new hashmap<string, bytearrayoutputstream>();         jaxbcontext.newinstance(clazz).generateschema(new schemaoutputresolver(){             @override             public result createoutput(string namespaceuri, string suggestedfilename) throws ioexception{                 bytearrayoutputstream bout = new bytearrayoutputstream();                 schemas.put(suggestedfilename, bout);                 streamresult result = new streamresult(bout);                 result.setsystemid(suggestedfilename);                 return result;             }         });          domlsinputlist lsinputlist = new domlsinputlist();         for(map.entry<string, bytearrayoutputstream> entry : schemas.entryset()){             bytearrayinputstream bin = new bytearrayinputstream(entry.getvalue().tobytearray());             lsinputlist.addstream(bin, entry.getkey(), null);         }         return new xsparser().parse(lsinputlist);     }      private static object unmarshal(class clazz, inputsource is) throws exception{         xsmodel xsmodel = generateschemas(clazz);         jaxbcontext context = jaxbcontext.newinstance(clazz);         saxparserfactory factory = saxparserfactory.newinstance();         factory.setnamespaceaware(true);         xmlreader xmlreader = factory.newsaxparser().getxmlreader();         xmlreader = new base64filter(xmlreader, xsmodel);         return context.createunmarshaller().unmarshal(new saxsource(xmlreader, is));     }      private static class base64filter extends xmlfilterimpl{         private xsmodel schema;         private list<qname> xpath = new arraylist();         private filewriter filewriter;         public base64filter(xmlreader parent, xsmodel schema){             super(parent);             this.schema = schema;         }          @override         public void startdocument() throws saxexception{             xpath.clear();             super.startdocument();         }          @override         public void startelement(string uri, string localname, string qname, attributes atts) throws saxexception{             super.startelement(uri, localname, qname, atts);              xpath.add(new qname(uri, localname));             xselementdeclaration elem = xsutil.findelementdeclaration(schema, this.xpath);             if(elem!=null){                 xstypedefinition type = elem.gettypedefinition();                 if(type.gettypecategory()==xstypedefinition.simple_type){                     xssimpletypedefinition simpletype = (xssimpletypedefinition)type;                     while(!namespaces.uri_xsd.equals(simpletype.getnamespace()))                         simpletype = (xssimpletypedefinition)simpletype.getbasetype();                     if("base64binary".equals(simpletype.getname())){                         try{                             file file = file.createtempfile("data", "binary");                             file.deleteonexit();                             filewriter = new filewriter(file);                             string absolutepath = file.getabsolutepath();                             super.characters(absolutepath.tochararray(), 0, absolutepath.length());                         }catch(ioexception ex){                             throw new saxexception(ex);                         }                     }                 }             }         }          @override         public void characters(char[] ch, int start, int length) throws saxexception{             try{                 if(filewriter==null)                     super.characters(ch, start, length);                 else                     filewriter.write(ch, start, length);             }catch(ioexception ex){                 throw new saxexception(ex);             }         }          @override         public void endelement(string uri, string localname, string qname) throws saxexception{             xpath.remove(xpath.size() - 1);             try{                 if(filewriter!=null)                     filewriter.close();                 filewriter = null;             }catch(ioexception ex){                 throw new saxexception(ex);             }             super.endelement(uri, localname, qname);         }     }; } 

now read xml file below:

public static void main(string[] args) throws exception{     documenttemplatefile obj = (documenttemplatefile)unmarshal(documenttemplatefile.class, new inputsource("sample.xml"));     // obj.data refers file contains base64 encoded data } 

Comments