Solving the JAXB "unexpected element" problem
If you are using JAXB in a maven/java project to unmarshal an XML document and you get:
javax.xml.bind.UnmarshalException: unexpected element (uri:"urn:iso:std:somestuff:xsd:somestuff", local:"Document"). Expected elements are (none)
Or if you are using JAXB to marshal an XML document and you get:
com.sun.istack.internal.SAXException2: unable to marshal type "generated.somestuff.Document" as an element because it is missing an @XmlRootElement annotation
You have probably fallen victim of the fact that JAXB does not do "Simple Binding" by default. If your project is a maven project and you generated classes based on an xsd file, this is how you fix it (without changing the xsd file):
Tell JAXB to do "simple binding". To do so, create a binding file called src/main/resources/jaxb/simple-binding.xml with the following contents:[sourcecode language="xml"] <jaxb:bindings jaxb:extensionBindingPrefixes="xjc" version="2.1" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"> jaxb:globalBindings xjc:simple/ </jaxb:globalBindings> </jaxb:bindings> [/sourcecode]
Then, add the following options to the execution of your jaxb2-maven-plugin (highlighted):
[sourcecode language="xml" highlight="13,14,15"]
You should be able to re-generate the jaxb code now, and parsing should work. In order to test if you have this problem, and to ensure that it does not come back after somebody changes your build file, create a unittest which marshals a java object into xml, and then unmarshalls the generated xml back into an object:
[sourcecode language="java"] // Create document objects (JAXB Generated classes) Document sourceDocument = new Document(); sourcedocument.setSomeElement(new SomeElement());
JAXBContext jaxbContext = JAXBContext.newInstance(Document.class);
// Write Document File file = File.createTempFile("sometempstuff", ".xml"); FileWriter fileWr = new FileWriter(file); XMLOutputFactory xml = XMLOutputFactory.newFactory(); XMLStreamWriter xmlStrWr = xml.createXMLStreamWriter(fileWr); xmlStrWr.writeStartDocument("UTF-8", "1.0"); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); marshaller.marshal(sourceDocument, xmlStrWr); xmlStrWr.writeEndDocument(); fileWr.close();
// Read Document FileInputStream fileInpStr = new FileInputStream(file); Unmarshaller unm = jaxbContext.createUnmarshaller(); Document result = (Document) unm.unmarshal(fileInpStr);
// Test the parsedDocument here if you like. ...
// Cleanup file.delete(); [/sourcecode]
The reason for this blog is of course the fact that I expected this "Simple Binding" to be the default, as 80% of the people will want to do exactly this. I've found a lot of people suffering from this problem, and I found the documentation to be not very helpful.
I hope this blog post helps you get on with more important stuff.
Have fun! Rolf