Message Object
Our message object needs to represent 3 things:
- Who the message is from.
- Who the message is to.
- The body of the message.
package message; import javax.xml.bind.annotation.*; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Message { @XmlAttribute private String to; @XmlAttribute private String from; @XmlAnyElement(lax=true) private Object body; }Customer Payload
The Customer class will be the root type for the body of the message when the payload corresponds to customer information, so we need to annotate it as @XmlRootElement.
package customer; import javax.xml.bind.annotation.*; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Customer { private String name; private Address address; }
package customer; import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) public class Address { private String street; private String city; }
Product Payload
The Product class will be the root type for the body of the message when the payload corresponds to product information, so we need to annotate it with @XmlRootElement.
package product; import javax.xml.bind.annotation.*; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Product { private String name; }
JAXBContext.newInstance("message:customer:product");
Address Customer
JAXBContext.newInstance("message:customer:product:order");
<message to="john@example.com" from="jane@example.com"> <customer> <name>Sue Smith</name> <address> <street>123 A Street</street> <city>Any Town</city> </address> </customer> </message>
import java.io.File; import javax.xml.bind.*; import message.Message; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance("message:customer:product"); Unmarshaller unmarshaller = jc.createUnmarshaller(); File file = new File("input.xml"); Message message = (Message) unmarshaller.unmarshal(file); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(message, System.out); } }
Thanks blaise. I don't suppose you could give an example of how to marshal this XML using this object structure could you? I want to do the reverse: Inject some fairly freeform XML into a field of type @XmlAnyElement.
ReplyDeleteWith this example you could create an instance of Message and set an instance of Customer or Product on it. You can also set an instance of org.w3c.dom.Element on it, if you wish to include free form XML.
ReplyDeleteIf you always want the content of the property to be a DOM node you have specify that lax=false on the @XmlAnyElement annotation.
Once I have the message object,
ReplyDeleteMessage message = (Message) unmarshaller.unmarshal(file);
What is the best way to get the Customer object out of the Message object? Do I have to check for instanceof against all object types that I have and cast it to the proper type? Eg
if(message.getBody() instanceof Customer) {
Customer customer = (Customer) message.getBody();
}
I say this because I am working on an extensible REST api with tons of objects, and thats a lot of boilerplate code that I have to write.
Since with JAXB you have control over you objects, you could leverage a common super class or interface with known API that will eliminate the need for doing the instanceof checks.
ReplyDelete-Blaise
Many frameworks are classpath aware? Why do you think JAXB hasn't taken this approach and done away with the ObjectFactory?
ReplyDeleteHi bballing,
DeleteIs there a particular framework that you could point to as an example?
-Blaise
Say in the springframework you are able to add @Component to a class, then in the applicationContext.xml you add it scans that class path looking for classes with @Component annotations.
ReplyDelete- Josh
Hi Blaise,
ReplyDeleteI am working with CXF 2.3.6 with default JAXB binding, and marshalling of interfaces works using the technique mentioned in this post. However, unmarshalling fails with the error
2012-09-06 17:36:00,264 [qtp1223223487-31] ERROR - javax.ws.rs.WebApplicationException: java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to com.emeter.config.mgmt.model.jaxb.valueholder.IValueHolder
javax.ws.rs.WebApplicationException: java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to com.emeter.config.mgmt.model.jaxb.valueholder.IValueHolder
It doesn't work with lax=true either. I do not have much control over creating the JAXBContext with CXF. I do not want to override too much in the framework, just use whatever configuration is provided. I need the interface to be extensible by adding more classes (other developers, different jars) in future.
I read this http://blog.bdoughan.com/2010/07/moxy-jaxb-map-interfaces-to-xml.html, and after reading your comment dated March 6, 2012 in that post, now I am confused whether unmarshalling of interfaces even works with JAXB RI, or I have to necessarily use Moxy.
If I necessarily have to use Moxy, how do I instruct CXF to use Moxy and not use JAXB RI.
regards,
Kartik
Hi Kartik,
DeleteIn JAX-RS frameworks a ContextResolver can be used to build a JAXBContext that is aware of all the model classes that it needs to be. I'm assuming that objects are coming back as DOM elements because JAXB has processed the classes with the corresponding @XmlRootElement annotations. You can find a ContextResolver example here:
- MOXy's XML Metadata in a JAX-RS Service
MOXy has some added support for handling interfaces. The following example will work with any JAXB (JSR-222) implementation:
- JAXB and Interface Fronted Models
-Blaise
Hi Blaise ,
ReplyDeleteDo you have solution for http://stackoverflow.com/questions/18036811 ?
I have posted an answer to your question:
Delete- http://stackoverflow.com/a/18070126/383861