The Source for Java Technology Collaboration
User: Password:



Kohsuke Kawaguchi

Kohsuke Kawaguchi's Blog

Why does JAXB put @XmlRootElement sometimes but not always?

Posted by kohsuke on March 03, 2006 at 04:21 PM | Comments (15)

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:


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

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):

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

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 <xjc:simple/> customization):


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

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 ));

Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • I never use anonymous types. As such, mine always generates that ObjectFactory method that returns a JAXBElement....

    My first XJC Plugin was designed specifically to fix this problem. I have an interface:

    public interface JAXBtoElement
    {
    public JAXBElement toElement();
    }


    Then, my XJC Plugin just makes the one class implement the interface like:

    public class DBConfig
    implements JAXBtoElement

    public JAXBElement toElement() {
    return (new ObjectFactory()).createMimirdb(this);
    }


    Then, my marshaller can take the DBConfig object (instead of ObjectFactory) and just marshall it:

    createMarshaller().marshal(obj.toElement(), new FileOutputStream(file));

    I could clean up this XJC Plugin and contribute it to the jaxb-commons... but maybe adding this little piece would be easier for the community at large? (we can reword of course)....

    Posted by: malachid on March 07, 2006 at 08:11 AM

  • btw: sorry, I always forget that it doesn't accept text formatting -- let me try to redo to make it more readable.

    I never use anonymous types. As such, mine always generates that ObjectFactory method that returns a JAXBElement....

    My first XJC Plugin was designed specifically to fix this problem.
    I have an interface:

    public interface JAXBtoElement { public JAXBElement toElement(); }

    Then, my XJC Plugin just makes the one class implement the interface like:

    public class DBConfig implements JAXBtoElement<DBConfig>

    public JAXBElement toElement()

    {

       return (new ObjectFactory()).createMimirdb(this);

    }

    Then, my marshaller can take the DBConfig object (instead of ObjectFactory) and just marshall it:

    createMarshaller().marshal(obj.toElement(), new FileOutputStream(file));

    I could clean up this XJC Plugin and contribute it to the jaxb-commons... but maybe adding this little piece would be easier for the community at large? (we can reword of course)....

    Posted by: malachid on March 07, 2006 at 08:15 AM


  • Have you tried the simple binding mode? It should make your binding better.

    As for your plugin, I think it would be good to add this to jaxb2-commons. What do you want the name to be?

    Posted by: kohsuke on March 07, 2006 at 10:21 AM

  • I think the workaround:
    marshaller.marshal( new JAXBElement(
    new QName("uri","local"), MessageType.class, messageType ));

    needs a second parameter for the output.

    Posted by: kalyson on May 26, 2006 at 09:21 AM

  • A little more detail on the programmatic solution you offer at the end article would be useful. I would favour the programmatic solution over adding an extension elements in the XML schema and command line flags in my build script - or is there a good reason to do it the other way around?

    You mention in a response above that using the simple binding mode extension should make binding better. In what way would it be better?

    Posted by: paulpepper on November 03, 2006 at 06:29 AM

  • That last snippet is a life-saver. Thanks! i just enhanced the support for JAXB-RI in Axis2.

    Posted by: dims on November 23, 2006 at 12:37 PM

  • Yes, thanks for the final snippet. How, though, would you handle a type, referenced by MessageType, which also does not have @XmlRootElement annotation?
    For example: public class MessageType { private SomeType some_name; }
    And SomeType does not have @XmlRootElement annotation.

    Posted by: asanh on April 03, 2007 at 01:13 PM

  • asanh, such type do not need @XmlRootElement. Your example would just marshal out fine.

    Posted by: kohsuke on April 04, 2007 at 10:15 AM

  • Coming in pretty late to this discussion here - did the plugin by malachid discussed above ever make it into jaxb2-commons? If not, is it available anywhere? It looks like exactly what we need.

    Posted by: nzlemming on May 11, 2007 at 04:34 AM

  • Is there any way that we could reverse this preference and make the "simple" mapping behavior the default? It is often preferable to declare your types and elements separately in an XSD so that you can do inheritance of the types... this shouldn't prevent you from using the derived elements as root tags of a document. It seems like a very unexpected side-effect to me.

    Also, does this only apply to JAXB 2.1+? I don't have a lot of experience with JAXB yet and wondered if this was a recently introduced behavior.

    Posted by: sleger on June 06, 2007 at 11:51 AM

  • The simple binding is not described in the spec, so I cannot make it the default mode. I believe this feature is new in 2.1.

    Posted by: kohsuke on June 06, 2007 at 04:32 PM

  • 1. Can you show the use of xjc:simple in the globalBindings section of an external binding.xjb file? 2. Which version of Jaxb has the and where is it to be downloaded from; I have 2.0 and 'jaxb-ri-20070122'

    Posted by: sisjo on June 12, 2007 at 06:20 PM

  • Eeks. Just browsing and realized you asked me a question a year ago. So sorry about that.

    As for putting it in the jaxb-commons... I am currently reworking everything to use the newest version. I am going to see if I can use the jaxb-reflection so as to not use any plugins... not sure yet...

    As for the simple binding mode. I was just trying it out on the current drop, and noticed something. Whereas normal mapping might create a TestType class, the simple binding creates a TestElement class (which states it is the TestType in the comments)... but it creates the same number of classes...

    Posted by: malachid on June 25, 2007 at 10:14 PM

  • I' trying to create SOAP messages with complex types, but i don't see how to make serialization using JAXB.
    Is there a means to serialize/deserialize such class in xml:

    public class MyClass{

    public Calendar cal;
    public CookieHandler ch;
    public ClassLoader cll;

    }

    When I use WsGen to generate XSD, the class "CookieHandler" is duplicated with a new empty class.
    In fact I'd like to extract only the part of JAX-WS API that is used for serialization/deserialization.

    Posted by: ds5 on June 27, 2007 at 04:51 AM

  • I' trying to create SOAP messages with complex types, but i don't see how to make serialization using JAXB.
    Is there a means to serialize/deserialize such class in xml:

    public class MyClass{

    public Calendar cal;
    public CookieHandler ch;
    public ClassLoader cll;

    }

    When I use WsGen to generate XSD, the class "CookieHandler" is duplicated with a new empty class.
    In fact I'd like to extract only the part of JAX-WS API that is used for serialization/deserialization.

    Posted by: ds5 on June 27, 2007 at 04:52 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds