JAXB to unmarshall <string>foobar</string> - java

Greetings! I have a server returning XML content to my client that looks like this:
<string xmlns="...">foobar</string>
I'm new to JAXB and have gotten a lot of stuff working, except this. I thought it would be pretty easy to marshal and unmarshal this to and from a String. It took a while, but I finally figured out how to marshal this as
public static String ToXML(String s) throws Exception
{
JAXBContext context = JAXBContext.newInstance(String.class);
Marshaller marshaller = context.createMarshaller();
StringWriter sw = new StringWriter();
marshaller.marshal(new JAXBElement(new QName("", "String"), String.class, s), sw);
return sw.toString();
}
So my question is, how to I unmarshal this? It cannot be annotated as a root element. I cannot use java.lang as the package to create a new instance of a JAXBContext (I get an ObjectFactory missing exception).
Any wisdom to impart? This can't be that hard, right?

You need to write an object model that conforms to your XML structure, and tell JAXB to unmarshal on to that. Your example may look simple, but it's not what JAXB is for.
Try something like this:
#XmlRootElement(name="string", namespace="blah")
public class MyString {
#XmlValue
String value;
}
JAXBContext context = JAXBContext.newInstance(MyString.class);
MyString myString = (MyString) context.createUnmarshaller().unmarshal(...);
This will unmarshal the XML <string xmlns="blah">foobar</string>. Change the namespace accordingly. If you have many namespaces, then JAXB isn't really the tool for you.

I was surprised by this, but the following seems to work:
final String xmlString = "<string>value</string>";
final StringReader xmlReader = new StringReader(xmlString);
final StreamSource xmlSource = new StreamSource(xmlReader);
final JAXBContext jaxbContext = JAXBContext.newInstance(String.class);
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
final String stringValue = unmarshaller.unmarshal(xmlSource, String.class).getValue();
Is that what you were looking for?

When you're using JAXB, you need to build the code around an XML schema. That is, if you have a file, say foo.xsd, you need to run it through the xjc compiler (in JDK 6 by default, otherwise you can download JAXB 2 and use that). That will read through the schema and generate the Java bean and associated ObjectFactory classes with the elements in the schema. The Java bean classes will look like regular POJOs with annotations. The ObjectFactory classes are needed by the JAXB implementation to convert the XML into the corresponding Java bean. This explains your ObjectFactory missing exception.
So it's not hard, but there is some leg work involved. We use it for one of our production applications and it's great. I see myself using it more now that it's part of the JDK.

Related

Create Marshaller for JSON from unknown JAXBContext (XML)

I have to use a lib that only gives me a JAXBContext to marshall and unmarshall XML data to Java objects. Also I don't ever see XML: Only the JAXB objects are passed to me. What I now need is a conversion of those objects not to XML, but to JSON.
Is there a way to create a marshaller from the given JAXBContext that can be used to generate JSON output?
The situation is that I'm not only transforming data. I also have logic that acts on the Java objects between XML and JSON (and manipulates the data). Also it's a two-way transformation. The JAXBContext is the information I have about the transformation between XML and Java objects - My intention was to reuse this context information for not having to implement a second transformation with a second technology different to JAXB. The JAXBContext (and its Java objects) already have the information about the XML structure; The automated recognition of that structure by JAXB is the time-saving reason for using it.
If your JAXB classes just use the basic annotations, you can take a look at JacksonJAXBAnnotations, allows Jackson mapper to recognize JAXN annotations. Four lines of code (in the simplest marshalling case) would be all you would need.
ObjectMapper mapper = new ObjectMapper();
JaxbAnnotationModule module = new JaxbAnnotationModule();
mapper.registerModule(module);
mapper.writeValue(System.out, yourJaxbObject);
You can see the link above for all the supported annotations. The maven artifact you'll need is
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>2.4.0</version>
</dependency>
See the github for jackson-module-jaxb-annotations - Note this artifact has dependencies on jackson-core and jackson-databind. So if you're not using maven, then you will need to make sure to download these artifacts also
Simple Exmaple:
JAXB Class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"hello",
"world"
})
#XmlRootElement(name = "root")
public class Root {
#XmlElement(required = true)
protected String hello;
#XmlElement(required = true)
protected String world;
// Getters and Setters
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<root>
<hello>JAXB</hello>
<world>Jackson</world>
</root>
Test
public class TestJaxbJackson {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream is = TestJaxbJackson.class.getResourceAsStream("test.xml");
Root root = (Root)unmarshaller.unmarshal(is);
System.out.println(root.getHello() + " " + root.getWorld());
ObjectMapper mapper = new ObjectMapper();
JaxbAnnotationModule module = new JaxbAnnotationModule();
mapper.registerModule(module);
mapper.writeValue(System.out, root);
}
}
Result
{"hello":"JAXB","world":"Jackson"}
Update
Also see this post. It looks like MOXy also offers this support.

JAXB marshall ignore interfaces

I'm trying to marshall a class with an interface in it. I got an error saying "JAXB can't handle interfaces.". I've seen several solutions for handling the interfaces, but I think my case is slightly different, because I'm not going to use the data in that interface in the xml. So is there any way to ignore the interface or something like that?
Also, the class (Comment) is got from a library, I don't think I can change the annotation.
Comment newComment = event.getComment();
// Marshalling
JAXBContext context = JAXBContext.newInstance(newComment.getClass());// error occurs
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(newComment, writer);

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.

In-memory unmarshalling, from an XML string and its schema stored in a string too, possible?

Let's suppose I store an XML string into a variable
String resp = new String("<?xml version=\"1.0\" encodin...");
and the schema definition related to this XML in another one:
String xsd = new String("<xs:schema xmlns="http://schema-...");
do you think there is a way to validate and unmarshall 'resp'
into objects (using JAXB for example) ?? Has anybody already
tried or successfully implemented such stuff ??
In advance, thanks a lot for any suggestion...
Seb
You can use unmarshal(Source source) and setSchema(Schema schema) of the Unmarshaller class. This should work:
unmarshaller.setSchema(SchemaFactory.newSchema(new StreamSource(new StringReader(xsd));
unmarshaller.unmarshal(new StreamSource(new StringReader(resp));

How to stream large Files using JAXB Marshaller?

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.

Categories