Thursday, 28 February 2013

@XmlSeeAlso

This is quite simple but very handy.

I am working with an XML structure that doesn't lend itself particularly well to being implemented in JAXB. The XML has a common outer element but the content varies by message type. There is no XSD for the XML but a reference document that (roughly) describes the schema.

I modeled this as

@XmlRootElement(name="apimessage")
class Message
{
...
    @XmlAnyElement(lax=true)
    public MessageBody getBody()
   ...
}

interface MessageBody
{
}

Then I have sub-classes of MessageBody like this:

@XmlRootElement(name="bodya")
class BodyA implements MessageBody
{
}

I hit the first problem while testing which was that when decoding a message the decoder knew nothing about the body so you end up with errors like 


java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to com.xxx.MessageBody

I got around this by passing the sub-class types as parameters to the JAXBContext when I was initializing it.

Now this wasn't the end of my problems as these messages get passed over a web interface.. The error in that case was:

[com.sun.istack.SAXException2: class com.xxx.BodyA nor any of its super class is known to this context.

The solution is actually quite simple (although not very extensible) and that is you use the @XmlSeeAlso attribute to tell JAXB to register the sub-classes of MessageBody when the Message class is registered:


@XmlRootElement(name="apimessage")
@XmlSeeAlso({BodyA.class,BodyB.class})

class Message
{
...

And then it just works...

No comments:

Post a Comment