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
Post a Comment