The Problem I'm facing is how to marshall a large list of objects into a single XML File, so large I can not marshall the complete list in one step. I have a method that returns these objects in chunks, but then I marshall these using JAXB, the marshaller returns with an exception that these objects are no root elements. This is ok for the normal case there you want to marshall the complete document in one step, but it also happens if I set the JAXB_FRAGMENT Property to true.
This is the desired XML output:
<rootElem> <startDescription></startDescription> <repeatingElem></repeatingElem> <repeatingElem></repeatingElem>... </rootElem>
So I assume I need some kind of listener that dynamically loads the next chunk of repeatingElements to feed it to the marshaller before he would write the closing tag of the rootElement. But how to do that? Up until now I only used JAXB to marshall small files and the JAXB documentation does not give much hints for that use case.
A: The JAXB Specification currently does not address the thread safety of any of the runtime classes. In the case of the Oracle JAXB RI, the JAXBContext class is thread safe, but the Marshaller , Unmarshaller , and Validator classes are not thread safe.
In JAXB, marshalling involves parsing an XML content object tree and writing out an XML document that is an accurate representation of the original XML document, and is valid with respect the source schema. JAXB can marshal XML data to XML documents, SAX content handlers, and DOM nodes.
JAXB. The JAXB Marshaller interface is responsible for governing the process of serializing Java content trees i.e. Java objects to XML data. This marshalling to XML can be done to variety of output targets.
I'm aware that this is an old question but I came across it while searching for duplicates of another similar question.
As @skaffman suggests, you want to Marshal with JAXB_FRAGMENT
enabled and your objects wrapped in JAXBElement. You then repeatedly marshal each individual instance of the repeated element. Basically it sounds like you want something roughly like this:
public class StreamingMarshal<T> { private XMLStreamWriter xmlOut; private Marshaller marshaller; private final Class<T> type; public StreamingMarshal(Class<T> type) throws JAXBException { this.type = type; JAXBContext context = JAXBContext.newInstance(type); Marshaller m = context.createMarshaller(); m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); } public void open(String filename) throws XMLStreamException, IOException { xmlOut = XMLOutputFactory.newFactory().createXMLStreamWriter(new FileOutputStream(filename)); xmlOut.writeStartDocument(); xmlOut.writeStartElement("rootElement"); } public void write(T t) throws JAXBException { JAXBElement<T> element = new JAXBElement<T>(QName.valueOf(type.getSimpleName()), type, t); marshaller.marshal(element, xmlOut); } public void close() throws XMLStreamException { xmlOut.writeEndDocument(); xmlOut.close(); } }
As you've discovered, if a class does not have the @XmlRootElement
annotation, then you can't pass an instance of that class to the marshaller. However, there is an easy way around this - wrap the object in a JAXBElement
, and pass that to the marshaller instead.
Now JAXBElement
is a rather clumsy beast, but what it does is contains the element name and namespace of the object that you want to marshal, information which would normally be contained in the @XmlRootElement
annotation. As long as you have the name and namespace, you can construct a JAXBElement
to wrap your POJO, and marshal that.
If your POJOs were generated by XJC, then it will also have generated an ObjectFactory
class which contains factory methods for building JAXBElement
wrappers for you, making things a bit easier.
You'll still have to use the JAXB_FRAGMENT
property for the repeating inner elements, otherwise JAXB will generate stuff like the XML prolog each time, which you don't want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With