As promised, the JAXB2.0 techtip and one other observation
By klichong on Jan 31, 2006
The techtip I wrote about JAXB2.0 is published now which describes two of the new features of JAXB 2.0 compared to JAXB 1.0. Hopefully, after reading this tip, you will check out the jaxb samples provided with Java WSDP 2.0 in the jaxb directory and look at other features of this 2.0 release. Don't forget, there is also some code you download and try that accompanies this tip. As always, any feedback is always appreciated. For those of you that are looking for a more introductory article on JAXB, you can refer to the JWSDP 1.6 tutorial on JAXB since as of today, the tutorial for JWSDP 2.0 is still not yet available.
I did find out something interesting when writing code for this techtip. In the example code, I do some unmarshalling with the invoice schema (that's creating a Java object from the XML document) and marshalling (Java object to XML).
Here is the relevant code:
JAXBContext jc = JAXBContext.newInstance( "techtip" );
Unmarshaller u = jc.createUnmarshaller();
JAXBElement poElement = (JAXBElement)u.unmarshal( new FileInputStream( "po.xml" ) );
PurchaseOrderType po = (PurchaseOrderType)poElement.getValue();
Marshaller m = jc.createMarshaller();
m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
OutputStream os = new FileOutputStream( "incorrectpo.xml" );
This looks reasonable, doesn't it? But why does the compiler complain? Here is the exact message:
javax.xml.bind.MarshalException - with linked exception [org.xml.sax.SAXException: unable to marshal type "techtip.PurchaseOrderType" as an element because it is missing an @XmlRootElement annotation]
Let's look at the javadoc:
public void marshal(java.lang.Object obj,
- Marshal the content tree rooted at obj into a Writer.
obj- The content tree to be marshalled.
writer- XML will be sent to this writer.
JAXBException- If any unexpected problem occurs during the marshalling.
MarshalException- If the
ValidationEventHandlerreturns false from its handleEvent method or the Marshaller is unable to marshal obj (or any object reachable from obj). See Marshalling objects.
java.lang.IllegalArgumentException- If any of the method parameters are null
Still looks reasonable, doesn't it?
Pay attention to the last line
I am marshalling the purchase order object, po, since I thought that this is the object that I got when I used the (PurchaseOrderType)poElement.getValue() call. The JAXBContext, or so I thought, should know about this PO object because of the invoice schema. So, it should be able to marshal this java Object, right?
Actually, no - and for a good reason. The correct line should read:
I should be using the poElement object from the unmarshalling rather than the PurchaseOrderType object (which is one of the generated classes after the xjc process - the JAXB binding).
For the explanation, I turn to Joe Fialli, one of the JAXB 2.0 specification leads (JSR 222) and served as the technical reviewer for the article.
"The JAXBContext knows the PurchaseOrderType. However, for "po" above, it does not know the xml element tag. The XmlElement tag can be statically associated with a class with @XmlRootElement. (How it is done with Java to Schema.) However, for schema to java, PurchaseOrderType was generated from a complex type definition. More than one xml element \*could\* use PurchaseOrderType. The above code can only work for "poe", not just for instances of PurchaseOrderType. Schema to Java does generate @XmlRootElement on classes generated for anonymous complex type defintions since that type can only have one @XmlRootElement. However, a named complex type definition in XML Schema typically can easily have a one to many relationship with xml elements that reference the complex type definition."
Ahh - this makes sense now. A good reason on why to use the poElement when marshalling instead of the PurchaseOrderType (po) object.