How to stream large Files using JAXB Marshaller? - java

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.

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);
this.marshaller = context.createMarshaller();
this.marshaller.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.

I don't know much of JAXB, so I can't help. But if you don't mind, I have a suggestion.
Writing XML is a lot easier than reading it, so an solution for your problem might be to use a more "low level" approach. Just write your own marshaller using one of the available open source libraries for XML. I think you can easily do what you want using dom4j.

Related

How to create multiple xml request by only changing one field?

I need help to find the approach of how to create xml multiple times while I will be changing only two fields everytime and rest of the fields would be same as now. Please tell me the way to do it in java?
This is the sample xml below:
I would be changing the value of <Id> and <Originator>
<TransactionBlk>
<Id>NIK</Id>
<CorrelationId />
<Originator>NIK</Originator>
<Service>GetIns</Service>
<VersionNbr>1</VersionNbr>
<VersionNbrMin>0</VersionNbrMin>
<MsgNm>Req</MsgNm>
<MsgFormatCd>XML</MsgFormatCd>
</TransactionBlk>
You can crate one class contain all this parameter as class variable, create getter and setter method. Create object of class set value by using setter method.
You can use JAXB API's class to convert your java object into XML format.
The JAXBContext class provides the client's entry point to the JAXB API.
It provides an abstraction for managing the XML/Java binding information
necessary to implement the JAXB binding framework operations: unmarshal,
marshal and validate.
Here is Doc reference for convert your Java object into XML.
Here are tutorial for same Tutorial Link
Sample Code :
#XmlRootElement(name="TransactionBlk_REQ",namespace="http://TransactionBlk.com")
#XmlAccessorType(XmlAccessType.FIELD)
public class TransactionBlk
{
#XmlElement(name = "Id")
private String id;
#XmlElement(name = "Originator")
private String Originator;
//Your getter and setter method.
}
TransactionBlk bean = new TransactionBlk();
//Set your parameter value here
StringWriter responseWriter = new StringWriter();
JAXBContext jaxbContext = JAXBContext.newInstance(TransactionBlk.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.marshal(bean, responseWriter);
String xmlStr = responseWriter!=null?responseWriter.toString():null;
You can use XSLT to transform XML.
If all you're doing is printing a "boilerplate" document with changes in those two values, a, you could use the DPH (Desperate Perl Hacker) approach: simply assemble it as text, pausing to print the values at the appropriate places. To be safe, you should pre-scan the values to make sure they don't contain the <, >, or & characters and escape those if you find them.
For something more complex, or if you want to start learning how to do it "properly", look at the standard XML APIs for Java: DOM (the Document Object Model, an in-memory tree model of a document), SAX (an event-stream view of a document), and JAXP (tools to take an XML document and parse it into DOM or SAX so you can read it, and to take DOM or SAX and write those out as XML syntax). JAXP also provides standard APIs for invoking XPath to search a document and XSLT to apply a stylesheet to a document, so taken together these cover a huge percentage of the basic operations on XML.
You might want to look at some tutorials on using Java to manipulate XML. I'm certainly biased, not least because they published one of my articles, but in my experience IBM's DeveloperWorks website (https://www.ibm.com/developerworks/xml/) has had better-than-average material for learning about XML and other standards.

Changing class without changing a schema

I have a xsd schema which I'm not allowed to change. It produce generated java classes.
Assume that classes looks as follows:
class Data {
protected List<Value> value;
...
}
class Value {
...
}
Now I need to have my own MyValue ideally to be extended from Value.
class MyValue extends Value {
Integer myOwnField1;
Long anotherProperty;
}
And be able to say unmarshaller to use MyValue instead of Value when it parses xml file.
Later I would be able to use the fact that MyValue can contain some useful new fields inside, make operations over them, change them, etc. So I want extend the functionality which I have in schema without changing it.
Is that somehow possible to replace Value by MyValue for unmarshaller?
Apart from the obvious way to create a Map where I can map object which was generated by unmarshaller with my own fields and properties in MyValue. I'd like to avoid this way.
Can you load the list using bean properties?
public void setValue(List<Value> value) {
this.value = ...convert to List<MyValue>...
}
You might be interested in unmarshalling by declared type: indeed, if you override your XSD from the root element to every child element you need to override, you could use a second argument in unmarshal method to sepecify your custom mapping.
<T> JAXBElement<T> unmarshal(Source source,
Class<T> declaredType)
throws JAXBException
[...] declaredType - appropriate JAXB mapped class to hold
source's xml root element
(see Unmarshaller javadoc for more detailed info)

Java: reading Xml configuration and applying to an object, what's a lightweight, simple solution

In our various applications, we have a hodge-podge of different methods used to read Xml configuration info and apply it to a Java object.
I am looking for a utility that, when given some Xml element, will automatically take any child element and set a corresponding property on the object to be configured (and of course handle any data conversion from String to correct standard Java data type).
I realize I am describing something a lot like JAXB (which I've used only a little, as part of a project to serialize/deserialize objects to Xml), so maybe it's the best solution? I just don't particularly want to be required to add the annotations to the class, and would rather it be assumed that any setter corresponds to any similarly named Xml element.
Any recommendations on what should be a standard way to do this would be appreciated. (And I'm fine if people say go back and read the JAXB docs, because that's the best solution.)
Thanks in advance.
Update: I did end up with JAXB, although it wasn't exactly painless. The main downside is that it is not case-insensitive (when you are dealing with config files, it's best not to require match by case). One other downsides are that you need to deploy 3 additional jars. I ended up with this code (maybe there is something more elegant):
public class JAXBConfigurator<T> {
private String filePath;
private Class<T> clazz;
public JAXBConfigurator(Class<T> toConfigure, String xmlFilePath) {
this.clazz = toConfigure;
this.filePath = xmlFilePath;
}
/**
* #return Parses Xml and reads configuration from Document element. Using this method assumes that the
* configuration xml starts at the top of the xml document.
* #throws Exception
*/
public T createAndConfigure() throws Exception {
return createAndConfigure(null);
}
/**
* Selects specified element from the parsed Xml document to use as the base
* for reading the configuration.
*
* #param tagName
* #return
*/
public T createAndConfigure(String tagName) throws Exception {
Document doc = XmlUtils.parse(filePath);
Node startNode;
if (tagName == null) {
startNode = doc;
} else {
startNode = XmlUtils.findFirstElement(doc, tagName);
}
return readConfigFromNode(startNode);
}
private T readConfigFromNode(Node startNode) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<T> configElement = unmarshaller.unmarshal(startNode, clazz);
return configElement.getValue();
}
}
The class gets used like this:
JAXBConfigurator<MyConfig> configurator = new JAXBConfigurator<Config>(MyConfig.class, xmlfilePath);
instance = configurator.createAndConfigure("MyXmlStartTag");
...which seems reusable enough for most scenarios. Thanks again to everyone who responded.
JAXB (JSR-222) is configuration by exception. This means that no annotations are required. You just need to add metadata to override the default rules:
For an Example
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/TheBasics
Pick one of these: Castor, XMLBeans, JiBX, XStream and JAXB. I'd recommend JAXB or JiBX. And in case you use Spring for other purpose, you can also check out Spring Object/XML mapping, which is basically a wrapper around aforementioned implementations and provides a consistent API.

Printing a JAXB generated bean

I have a JAXB data class which is generated from wsimport and I'd like to print it to the console and/or log. Unfortunately a toString is not generated.
What's the easiest way to print the data object? It doesn't matter whether the output is the original XML or something else, as long as it's readable.
It looks like the class is a valid bean (properly named getters and setters) so anything that works with beans is probably fine too.
For printing to the console, try this:
jaxbContext.createMarshaller().marshal(jaxbObject, System.out);
To get it into a String, use a StringWriter:
StringWriter writer = new StringWriter();
jaxbContext.createMarshaller().marshal(jaxbObject, writer);
String xmlString = writer.toString();
To get the JAXBContext object you need to do the following:
JAXBContext jaxbContext = JAXBContext.newInstance(<WhateverClass>.class);
Where <WhateverClass> is the class literal for the type that jaxbObject is. You should also be able to do:
JAXBContext jaxbContext = JAXBContext.newInstance(jaxbObject.getClass());
Depending on where you are defining the context and your stylistic preference. JAXBContext is thread-safe so it is good to define one instance and share it. Marshaller and Unmarshaller make no such guarantees though. So they need to be created on demand.

How to stream XML data using XOM?

Say I want to output a huge set of search results, as XML, into a PrintWriter or an OutputStream, using XOM. The resulting XML would look like this:
<?xml version="1.0" encoding="UTF-8"?>
<resultset>
<result>
[child elements and data]
</result>
...
...
[1000s of result elements more]
</resultset>
Because the resulting XML document could be big (hundreds of megabytes, perhaps), I want to output it in a streaming fashion (instead of creating the whole Document in memory and then writing that).
The granularity of outputting one <result> at a time is fine, so I want to generate one <result> after another, and write it into the stream. In other words, I'd simply like to do something like this pseudocode (automatic flushing enabled, so don't worry about that):
open stream/writer
write declaration
write start tag for <resultset>
while more results:
write next <result> element
write end tag for <resultset>
close stream/writer
I've been looking at Serializer, but the necessary methods, writeStartTag(Element), writeEndTag(Element), write(DocType) are protected, not public! Is there no other way than to subclass Serializer to be able to use those methods, or to manually write the start and end tags directly into the stream as Strings, bypassing XOM altogether? (The latter wouldn't be too bad in this simple example, but in the general case it would get quite ugly.)
Am I missing something or is XOM just not made for this?
With dom4j I could do this easily using XMLWriter - it has constructors that take a Writer or OutputStream, and methods writeOpen(Element), writeClose(Element), writeDocType(DocumentType) etc. Compare to XOM's Serializer where the only public write method is the one that takes a whole Document.
(This is related to my question about the best dom4j replacement where XOM is a strong contender.)
I ran in to the same issue, but found it's pretty simple to do what you mentioned as an option and subclass Serializer as follows:
public class StreamSerializer extends Serializer {
public StreamSerializer(OutputStream out) {
super(out);
}
#Override
public void write(Element element) throws IOException {
super.write(element);
}
#Override
public void writeXMLDeclaration() throws IOException {
super.writeXMLDeclaration();
}
#Override
public void writeEndTag(Element element) throws IOException {
super.writeEndTag(element);
}
#Override
public void writeStartTag(Element element) throws IOException {
super.writeStartTag(element);
}
}
Then you can still take advantage of the various XOM config like setIdent, etc. but use it like this:
Element rootElement = new Element("resultset");
StreamSerializer serializer = new StreamSerializer(out);
serializer.setIndent(4);
serializer.writeXMLDeclaration();
serializer.writeStartTag(rootElement);
while(hasNextElement()) {
serializer.write(nextElement());
}
serializer.writeEndTag(rootElement);
serializer.flush();
As far as I know, XOM doesn't support streaming directly.
What I used when I wanted to stream my XML documents was NUX, which has Streaming XML Serializer, similar to standard Serializer class in XOM. NUX is compatible with XOM. I downloaded NUX sources, extracted few NUX classes (StreamingSerializer interface, StreamingXMLSerializer -- which works for XOM documents, StreamingVerifier and NamespacesInScope), put them into my project, and it works like a charm. Too bad this isn't directly in XOM :-(
NUX is very nice companion to XOM: http://acs.lbl.gov/software/nux/, working mirror download: nux-1.6.tar.gz
Link to API: http://acs.lbl.gov/software/nux/api/nux/xom/io/StreamingSerializer.html
Here is sample code (methods are called in order: start(), n*nextResult(), finish(), serializer is StreamingXMLSerializer from NUX):
void start() {
serializer.writeXMLDeclaration();
Element root = new Element("response");
root.addAttribute(new Attribute("found", Integer.toString(123)));
root.addAttribute(new Attribute("count", Integer.toString(542)));
serializer.writeStartTag(root);
serializer.flush();
}
void nextResult(Result result) {
Element element = result.createXMLRepresentation();
serializer.write(element);
serializer.flush();
}
void finish() {
serializer.writeEndTag();
serializer.flush();
}

Categories