users@jersey.java.net

Re: [Jersey] A Christmas Quiz: How to solve weird JAXB Unmarshalling Problem?

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Tue, 29 Dec 2009 11:19:35 +0100

Hi Markus,

A belated Merry Christmas to you.

I will answer the question with another question :-)

What is the Content-Type request header that the client sends?


I think the following is happening. The WebDavContextResolver is not
being utilized because the Content-Type is not "application/xml". Note
that you have @Produces("application/xml") on WebDavContextResolver.

Jersey will create a default JAXBContext from LockInfo.class. Since
LockInfo has a static reference to Owner that too will be included in
the JAXBContext, but because there is no static reference to HRef it
will not be included in the JAXBContext.

Paul.

On Dec 25, 2009, at 5:22 PM, Markus Karg wrote:

> Hello Jersey Community,
>
> here is a Jersey Christmas Quiz for you: Why is our code not working?
>
> We detected a weird problem with JAXB Unmarshalling in Jersey and
> just don't know how to go on. :-(
>
> The problem is quite complex to explain, so sorry for the length of
> this email.
>
> We have the following three simple and short JAXB objects (shortened
> for easier readability):
>
> @XmlRootElement(name = "lockinfo")
> public class LockInfo {
> @XmlElement
> private Owner owner;
> public Owner getOwner() {return owner;}
> }
>
> @XmlRootElement
> public class Owner {
> @XmlMixed @XmlAnyElement(lax = true)
> private LinkedList<Object> any;
> public List<Object> getAny() {return (List<Object>)
> any.clone();}
> }
>
> @XmlRootElement(name = "href")
> public class HRef {
> @XmlValue
> private String value;
> public final String getValue() {return this.value;}
> }
>
> All of them are referenced in our JAXB Context:
>
> @Provider @Produces(MediaType.APPLICATION_XML)
> public class WebDavContextResolver implements
> ContextResolver<JAXBContext> {
> private JAXBContext context;
> public WebDavContextResolver() throws JAXBException {
> context = JAXBContext.newInstance(HRef.class,
> LockInfo.class, Owner.class);
> }
> @Override
> public JAXBContext getContext(Class<?> cls) {return context;}
> }
>
> public class AddressBookApplication extends Application {
> @Override
> public Set<Class<?>> getClasses() {
> return new HashSet<Class<?
> >>(Arrays.asList(AddressBook.class));
> }
>
> @Override
> public Set<Object> getSingletons() {
> try {
> return new HashSet<Object>(Arrays.asList(new
> WebDavContextResolver()));
> } catch (JAXBException e) {return null;}
> }
> }
>
> So far it looks all correct.
>
> When a client is sending a XML body like this one
>
> <lockinfo><owner><href>foo\bar</href></owner></lockino>
>
> what we expect to get in our resource's lockInfo variable obtained
> by @LOCK public void lock(LockInfo lockInfo) should be a JAXB
> object tree like this one:
>
> LockInfo
> !
> +--- Owner
> !
> +--- HRef
>
> But what we actually get is this one:
>
> LockInfo
> !
> +--- Owner
> !
> +--- Simple DOM Element with name 'href' and content 'foo\bar'
>
> We would understand to get a DOM Element if the any member wouldn't
> be annotated by lax = true -- but actually it is annotated that way!
>
> We would also understand to get a complete DOM-only tree like
>
> Simple DOM Element with name 'lockinfo'
> !
> +--- Simple DOM Element with name 'owner'
> !
> +--- Simple DOM Element with name 'href' and content 'foo\bar'
>
> but actually both container elements are correctly unmarshalled to
> JAXB elements.
>
> And it is getting even weird as soon as we replace our resource's
> method be this one:
>
> @LOCK
> public void lock(InputStream entityStream, @Context UriInfo uriInfo,
> @Context Providers providers, @ContextHttpHeaders httpHeaders)
> throws IOException {
> LockInfo lockInfo =
> providers .getMessageBodyReader(LockInfo.class, LockInfo.class, new
> Annotation[0],new
> MediaType.APPLICATION_XML_TYPE).readFrom(LockInfo.class,
> LockInfo.class, new Annotation[0], new
> MediaTypeAPPLICATION_XML_TYPE, httpHeaders.getRequestHeaders(),
> entityStream);
>
> Obviously this slightly modified code should come to the same result
> as the unmodified code, as the exact same MessageBodyReader (and
> this, JAXB context) is getting used. But that code absolutely
> correctly produces what we all want (and this is our current
> workaround), a 100% pure JAXB tree!
>
> LockInfo
> !
> +--- Owner
> !
> +--- HRef
>
> Ain't that really weird?
>
> If anybody has an idea how to solve this, please tell us! :-)
>
> Merry Christmas
> Markus