Paul,
I think I got it now. Because my XSD isn't putting any XmlRootElement in my nodes, I can't parse the object alone.. so the way I am doing it, passing in JAXBElement<MyClass> and returning those, is the "only" way I can do it based on the output of my XSD at this time. It may be that the XSD could be refactored a bit to generate the XmlRootElement in the appropriate class, but not quite sure yet. However, I am guessing if I did this every single API call that sends XML in, or returns XML, even small bits of it, would always need to be surrounded by the outer most element, that of which would have the @XmlRootElement in it.
Thank you for your quick work. I am not entirely sure what this does for me just yet, but I will be playing with it this week and giving you feedback.
----- Original Message ----
From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
To: users_at_jersey.dev.java.net
Sent: Monday, August 25, 2008 12:28:00 AM
Subject: Re: [Jersey] How to get JAXB entity in subresource
Hi Kevin,
There are two kinds of JAXB beans:
1) JAXB root element beans whose classes are annotated with @XmlRootElement. These correspond to XSD
elements; and
2) JAXB type beans whose classes are annotated with @XmlType. These correspond to XSD types.
Depending on how things are defined in a schema XJC may generate only of 2) and provide methods in the Object factory that return JAXBElement<T>.
For unmarshalling the JAXB unmarshaller can be supplied with a JAXB context that supports 1) or 2). If an XML type bean is returned by the unmarshaller then the root element of the XML document will be processed but that root element information will not be associated with the JAXB type bean. Or if the root element is required to be known for 2) then it is necessary to obtain a JAXBElement instance for the JAXB bean type using a specific method on the JAXB unmarshaller.
For marshalling it is required that JAXB have enough information to create a well formed XML document. Thus JAXB root element beans can be marshalled but JAXB type beans cannot, because it does not have a XML root element associated with it. In this case an instance of JAXBElement<T> needs to be used.
Jersey has supported 1) using JAXB root element bean instances and types and supported 2) using JAXBElement<T> instances and types where T is a JAXB type bean. However i have just added support for 2) in the trunk for unmarshalling (consuming) such that a JAXB type bean class can be referenced directly. This also solves for you the Client API issue.
As for the other issues, i think i am going to write a stand alone sample application for you that exercises various JAXB use cases and will help you understand what is going on and the best way to implement you services.
Paul.
On Aug 24, 2008, at 6:44 PM, Kevin Duffey wrote:
Hey all,
Two problems I am trying to figure out: First is how to "consume" a put/post body of xml in the subresource class:
@Path("/path")
class MyClass {
@GET
@Produces("application/xml")
public Response get(){
return Response.ok().build();
}
@Path("{page}")
public MySubClass getSubResource(@PathParam("page") String page){
return new MySubClass(page);
}
}
class MySubClass {
@PUT
@Consumes("application/xml")
public Response update(JAXBElement<MyJaxBGenClass> mjbgc) {
}
}
The problem is, everything I've tried, I am not seeing the MyJaxBGenClass getting passed to the subresource class. I've tried consuming on the getSubResource, tried using the JAXBElement parameter in the getSubResource() method itself. Every where I put the @Consumes and the JAXBElement parameter, it fails.
Second problem, which is "similar" to the first.. why do I have to use JAXBElement<MyJAXBClass> instead of MyJAXBClass in a method signature? :
@GET
@Produces({"application/xml", "text/xml", "application/json"})
public Response find(JAXBElement <MyJAXBGenClass> myclass){
}
The above always tells me it can't find a BodyReader fo the MyJAXBGenClass. Yet, it's generated with XJC and has the JAXB annotations in the XSD that generates the classes. Now, if Paul recalls, there is an issue on the Jersey Client where by the terminating method can not handle a JAXB Class directly. I am wondering if the same thing is happening here. For some reason my JAXB generated classes do not have an @XmlRootElement anywhere. Not even the "root" class. I am using the Ant XJC taskdef to generate these, so maybe I am not passing in a param to XJC that I should be? At any rate, I've seen some of the sample code that uses the JAXB generated classes directly in the method signuature:
@GET
@Produces("application/xml")
public Response find(MyJAXBGenClass myclass){
}
@PUT
@Consumes("appluication/xml")
public Response update(MyJAXBGenClass myclass){
}
So the above code should work, right? I shouldn't need to do any special JAXB stuff to turn the JAXBElement into an actual object? Right now, every method I use I have something like:
@GET
@Produces("application/xml")
public Response find(){
MyJAXBClass mc = new MyJAXBClass();
mc.set....
JAXBElement<MyClass> v = new ObjectFactory().createMyClass(mc);
return Response.ok(v).build();
}
and the reverse:
@PUT
@Consumes("application/xml")
public Response update(JAXBElement<MyClass> mc){
MyClass m = (MyClass)mc.getValue();
}
So.... I don't know why I have to keep doing it that way when some of the examples, the JAXBJSon one specifically show the actual class itself in the method as if the JAXB/Jersey knows how to handle it without me needing to do any JAXB getValue() or ObjectFactory.createClass() stuff.
I even copied the @Producer class from the sample, called JAXBContextResolver, and put my classes in there and it's still not working. I don't even know if that resolver class is required. But from my understanding using JAXB generated classes, the Annotations in the generated classes are all that Jersey needs to be able to do the JAXB conversions for me. I just seem to be missing something.
Thanks.