Skip to main content

Why does JAXB put @XmlRootElement sometimes but not always?

Posted by kohsuke on March 3, 2006 at 4:21 PM PST

isusanin asked in the JAXB forum:

When trying to marshall an object I get this error message:

unable to marshal type "org.blah.MessageType" as an element
because it is missing an @XmlRootElement annotation

If I add the @XmlRootElement annotation to the MessageType, it works, but my question is why doesn't the JAXB compiler put this annotation in automatically. It's inconvenient because MessageType is automatically generated and I don't want to edit it every time I regenerate the java classes.

Is there any better way to resolve this problem?

XJC does try to put @XmlRootElement annotation on a class that we generate from a complex type. The exact condition is somewhat ugly, but the basic idea is that if we can statically guarantee that a complex type won't be used by multiple different tag names, we put @XmlRootElement. So for example, if the schema is as follows:

<br /><schema><br />  <element name="foo"><br />    <complexType><br />      ...<br />    </complexType><br />  </element><br /></schema><br />

Then you'd get:

@XmlRootElement
class Foo {
  ...
}

But if the schema is as follows, then XJC can't eliminate the possibility that your type might be used in other schemas (that can be compiled separately and put together at runtime):

<br /><schema><br />  <element name="foo" type="bar" /><br />  <complexType name="bar" /><br /></schema><br />

So you'd get:

class Bar {
}
class ObjectFactory {
  JAXBElement<Bar> createFoo(Bar value) { ... }
}

Now, the crucial part in the above inference done by XJC is that "your schema might be used by other schemas that XJC isn't compiling right now". This is certainly true for some users (and we learned that the hard way), but it's also true that for many users this is too conservative an assumption. It's often the case that what you are compiling is indeed the whole thing, and you want XJC to know that so that it can optimize the generated code more aggressively.

Such notion isn't defined in the spec, but as an experiment we have such aggressive optimization mode in XJC, tentatively called "simple-minded binding mode". Consider the following schema that uses this mode (see that customization):

<br /><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"<br />  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"<br />  jaxb:version="1.0"<br />  xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"<br />  jaxb:extensionBindingPrefixes="xjc"><br />  <xs:annotation><br />    <xs:appinfo><br />      <jaxb:globalBindings><br />        <xjc:simple /><br />      </jaxb:globalBindings><br />    </xs:appinfo><br />  </xs:annotation><br />  <br />  <xs:element name="foo" type="bar" /><br />  <xs:complexType name="bar" /><br /></xs:schema><br />

From this you'll just get Foo class, since XJC now knows that only one element is using bar, and therefore there's no point in having two of them around (I just fixed a few issues wrt this, so if you want to try it, wait until tomorrow and download nightly.) Also note that this is an experimental mode and it is subject to change.

Now, that said, there's another way to work around this issue. If all you want is just to marshal an object without @XmlRootElement, then you can just do:

marshaller.marshal( new JAXBElement(
  new QName("uri","local"), MessageType.class, messageType ));
Related Topics >>

Comments

Issue with marshalling xsd any element

Issue with marshalling xsd any element

I have 2 xsd files Element from XSD A ...

I have 2 xsd files
Element from XSD A




element from XSD B







When I try to marshal "LifeObj" EXception I am getting is

[com.sun.istack.internal.SAXException2: unable to marshal type "packagebname.ProcessParameters" as an element because it is missing an @XmlRootElement annotation]

Code i am using is

ProcessParameters parametersType = new ProcessParameters();
parametersType.setTransactionID("TXID");
ExtensionObj eExtensionType = new ExtensionObj();
eExtensionType.getContent().add(parametersType);

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
File SEEC_Life_xsd = new File("xsd/Life.xsd");
Schema schema = schemaFactory.newSchema(Life_xsd);
JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class.getPackage().getName());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setSchema(schema);

marshaller.marshal(new JAXBElement(new QName(url,"ExtensionObj"), ExtensionObj.class, eExtensionType), System.out);

HI, I am facing problems in marshalling xsd any type ...

HI,
I am facing problems in marshalling xsd any type using jaxb. I have 2 xsd files and the corresponding jaxb objects generated by the xsds(Say xsd A and xsd B). xsd A has xml elements with XSD:Any tag. And I want to have the jaxb objects from xsd B in the xsd A elements. That time I am getting the SAXparser exception saying that the jaxb elements from xsd B does not have @XMLRootElement. How to solve this issue?

elemnt from xsd A


.
.
.

elemnt from xsd B







If I try to marshal "LifeObj" I am getting the exception
[com.sun.istack.internal.SAXException2: unable to marshal type "packagename.ProcessParameters" as an element because it is missing an @XmlRootElement annotation]
Code is
ProcessParameters parametersType = new ProcessParameters();
parametersType.setTransactionID("TXID");
eExtension eExtensionType = new eExtension();
eExtensionType.getContent().add(parametersType);

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
File SEEC_Life_xsd = new File("xsd/Life.xsd");
Schema schema = schemaFactory.newSchema(Life_xsd);
JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class.getPackage().getName());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setSchema(schema);

marshaller.marshal(new JAXBElement(new QName(url,"eExtension"), eExtension.class, eExtensionType), System.out);

With a Maven build, you can add the @XmlRootElement ...

With a Maven build, you can add the @XmlRootElement annotation with the "jaxb2-basics-annotate" plug-in.

See more information :
http://codereview.stackexchange.com/questions/1877/jaxb-xjc-code-generation-adding-xmlrootelement-and-joda-datetime

http://azagorneanu.blogspot.be/2011/09/configure-maven-to-generate-classes.html

<p>&nbsp;I had same issue while using SpringWebTemplate and ...

I had same issue while using SpringWebTemplate and fixed it in following way: springwstemplatejax2bxmlrootmissing.blogspot.com/

Unable to locate method

>Now, that said, there's another way to work around this issue. If all you want is just to marshal an object without @XmlRootElement, then you can just do:
>
>marshaller.marshal( new JAXBElement(
> new QName("uri","local"), MessageType.class, messageType ));


Hi Kohsuke
Thanks for the solution, please let me know the version in which I can use marshal(JAXBElement) method with these signatures.
Please suggets if I am doing it wrong.

(I am using JDK 1.6 JAXB)


-- Anish Sneh

Unable to locate method

Actually there is more stable solution. Use Annotate plugin from here confluence.highsource.org/display/J2B/Annotate+Plugin
in case of external binding file code would be like:
<jaxb:binding
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:annox="http://annox.dev.java.net"
>
...
<jaxb:bindings node="/xs:schema/xs:complexType[@name='YourRootElementComplexType']">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="YourRootElementName"/>
</annox:annotate>
</jaxb:bindings>

make sure correct jars are on classpath:
xjc.bat -classpath "jaxb2-basics-annotate-0.6.0.jar;commons-logging-1.1.1.jar;jaxb2-basics-tools-0.6.0.jar;annox-0.5.0.jar;commons-lang-2.2.jar -b "your_external_binding_file.xjb" -readOnly -extension -Xannotate r -p "your_package" -d "your_folder" -xmlschema "your_xsd" -verbose

same solution for external binding file

This was very helpful but what I really wanted to do was to achieve the same thing using an external binding file as opposed to polluting the schema with inline annotations.

This page seems to be referenced in many forums on the web by many people with similar problems. However, it took me a lot of searching to find what I was after.

Eventually I found this link (http://forums.java.net/jive/message.jspa?messageID=350060) which provided the following solution:

<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0" xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc">
<jaxb:globalBindings>
<xjc:simple/>
</jaxb:globalBindings>
</jaxb:bindings>

I hope this helps any JAXB newbies like myself.

Can't get it to generate @XmlRootElement

Thanks for the article first of all.. Its really helpful..
Version i am using : jaxb-xjc-2.1.10.jar.. so i change my jaxb:version to 2.1 from 1.0
(NOTE: I am removing the starting and engind traingular brackets it having some problem)

schema targetNamespace="http://www.abc.com/SRM" xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:tns="http://www.abc.com/SRM" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.1" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc"
annotation
appinfo
jaxb:globalBindings
xjc:simple /
/jaxb:globalBindings
/appinfo
/annotation

complexType name="NetworkAddress1"
sequence
element name="address" maxOccurs="1" minOccurs="1" type="string" /
element name="port" maxOccurs="1" minOccurs="1" type="short" /
/sequence
/complexType

/schema

I tried the above thing in my xsd file and it didn't work..
The generated file:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "NetworkAddress1", propOrder = {
"address",
"port"
})

public class NetworkAddress1 {
@XmlElement(required = true)
protected String address;
protected short port;

... REMOVED GET SET

build file:
mkdir dir="${generated.dir}/SRM1" />
xjc extension="true" destdir="${generated.dir}/SRM1" package="com.n2bb.srm.abc">
produces dir="${generated.dir}/SRM1/com/n2bb/srm/abc" includes="**/*.java" />
schema dir="${xsd.dir}" includes="SRM1.xsd" /
/xjc

Also i tried

jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0" xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc"
jaxb:globalBindings
xjc:simple/
/jaxb:globalBindings
/jaxb:bindings

But it gives me syntactical error as jaxb:bindings..
Could some help me out here ??

I tried using ObjectFactory

I tried using ObjectFactory which might work just as well, not just sure if it's a good practice. ObjectFactory obj = new ObjectFactory(); ... marshaller.marshal(obj.createMessage(jaxbObj), sw);

This was VERY helpful. Now I

This was VERY helpful. Now I need to do the reverse (unmarshal). For me it is XML for a "string" as in (brackets are odd due to comment syntax)... [string xmlns="uri"]foobar[/string] What should I do to make the unmarshaller happy. (As you can tell, I'm new to JAXB) Many thanks.

I found some information on unmarshalling, that helped me, ...

I found some information on unmarshalling, that helped me, here:
http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/tut...
(Found this thread while searching for a solution - so I'll post it here, even if the thread is old)

unmarshall non-root JAXB class

I also need to do the reverse (unmarshal). can you advise? thks, Lee!