Skip to main content

@XmlJavaTypeAdapter not working for parameter of web method

8 replies [Last post]
virtualpt
Offline
Joined: 2008-02-25
Points: 0

Hi I am having a problem getting my @XmlJavaTypeAdapter working when annotated on a parameter for a web method, I can successfully get one working, if I annotate a field on the parameter class. But when I annotate the parameter class the adapter is never called (the print lines in the method are never seen) & I get null values set in the Order object. Please what am I doing wrong? Why is the adapter never called?I am happy to attach source files if that's useful. Using jaxws-ri 2.1.3.

Thanks very much in advance :-)
Paul

have a web method as follows:

@WebMethod(operationName = "processOrder")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@WebResult(name = "orderReceipt", targetNamespace = "com.javacoda.jaxws.order")
public Receipt placeOrder(@WebParam(name = "webOrder", targetNamespace = "com.javacoda.jaxws.order") Order order);

#########################################

the Order parameter class is annotated at class level like so (this is the only annotation on the class):
@XmlJavaTypeAdapter(OrderAdapter.class)
public class Order {
private String orderNumber;
private Customer customer;
private List items;

#########################################

my OrderAdapter is as follows:
public class OrderAdapter extends XmlAdapter {

public Order unmarshal(TransitionalOrder transitionalOrder) throws Exception {
System.out.println("OrderAdapter.unmarshal");
Order order = new Order();
Customer customer = new Customer();
customer.setForename(transitionalOrder.getFirstName());
customer.setSurname(transitionalOrder.getLastName());
order.setCustomer(customer);
order.setOrderNumber(transitionalOrder.getOrderNumber());
order.setItems(transitionalOrder.getItems());
return order;
}

public TransitionalOrder marshal(Order order) throws Exception {
System.out.println("OrderAdapter.marshal");
TransitionalOrder transitionalOrder = new TransitionalOrder();
transitionalOrder.setFirstName(order.getCustomer().getForename());
transitionalOrder.setLastName(order.getCustomer().getSurname());
transitionalOrder.setOrderNumber(order.getOrderNumber());
transitionalOrder.setItems(order.getItems());
return transitionalOrder;
}
}

#########################################

and my 'transitional' class is annotated as follows:
@XmlType(name = "orderDetails", namespace = "com.javacoda.jaxws.order",
propOrder = {
"orderNumber",
"firstName",
"lastName",
"items"
})
public class TransitionalOrder {
private String orderNumber;
private String firstName;
private String lastName;
private List items;

@XmlElement(name = "order_number", namespace = "com.javacoda.jaxws.order", required = true)
public String getOrderNumber() {
return orderNumber;
}

public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}

@XmlElement(name = "forename", namespace = "com.javacoda.jaxws.order", required = true)
public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

@XmlElement(name = "surname", namespace = "com.javacoda.jaxws.order", required = true)
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@XmlElementWrapper(name = "order_items", namespace = "com.javacoda.jaxws.order",required = true)
@XmlElement(name = "order_item", namespace = "com.javacoda.jaxws.order",required = true)
public List getItems() {
return items;
}

public void setItems(List items) {
this.items = items;
}
}

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
virtualpt
Offline
Joined: 2008-02-25
Points: 0

As an addendum to this, I have tried declaring the adapter on the web method decalaration as follows:
@XmlJavaTypeAdapter(OrderAdapter.class)
public Receipt placeOrder(@WebParam(name = "webOrder", targetNamespace = "com.javacoda.jaxws.order")Order order);

however wsgen then errors with java.security.PrivilegedActionException: javax.xml.bind.JAXBException: com.javacoda.jaxws.order.TransitionalOrder is not known to this context.

I can't see why either of my examples don't work as according to the docs
The @XmlJavaTypeAdapter annotation can be used with the following program elements:

* a JavaBean property
* field
* parameter
* package
* from within XmlJavaTypeAdapters

When @XmlJavaTypeAdapter annotation is defined on a class, it applies to all references to the class.

When @XmlJavaTypeAdapter annotation is defined at the package level it applies to all references from within the package to @XmlJavaTypeAdapter.type().

When @XmlJavaTypeAdapter annotation is defined on the field, property or parameter, then the annotation applies to the field, property or the parameter only.

virtualpt
Offline
Joined: 2008-02-25
Points: 0

OK, I've found a way to get this to work, but there seems to be a bit of a nasty hack here ;-)

First I set the adapter on the method parameter like so:
@WebMethod(operationName = "processOrder")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@WebResult(name = "orderReceipt", targetNamespace = "com.javacoda.jaxws.order")
public Receipt placeOrder(@WebParam(name = "webOrder", targetNamespace = "com.javacoda.jaxws.order") @XmlJavaTypeAdapter(OrderAdapter.class)Order order);

If I only do this though I still get the javax.xml.bind.JAXBException: com.javacoda.jaxws.order.TransitionalOrder is not known to this context.

So on my Order class I have to add a field representing the TransitionalOrder so that the JAXB context knows about it like this:

public class Order {
private String orderNumber;
private Customer customer;
private List items;

@XmlElement
private TransitionalOrder transitionalOrder; // This has to be here for the JAXB context to know about it!!

This seems a bit hacky. Is there any other way for me to ensure that the JAXB context knows about the TransitionalOrder?

Thanks

Paul

virtualpt
Offline
Joined: 2008-02-25
Points: 0

OK, more on this :-)

It can also be made to work if the Order class extends the TransitionalOrder class. Looking at the api for JAXBContext - I'm guessing that the problem may be do with the way that the JAXBContext instance is created, in that I imagine when the context is created for the web method call it uses the second of the methods mentioned in the docs here, which uses the class (and any classes statically reachable):

A client application normally obtains new instances of this class using one of these two styles for newInstance methods, although there are other specialized forms of the method available:

* JAXBContext.newInstance( "com.acme.foo:com.acme.bar" )
The JAXBContext instance is initialized from a list of colon separated Java package names. Each java package contains JAXB mapped classes, schema-derived classes and/or user annotated classes. Additionally, the java package may contain JAXB package annotations that must be processed. (see JLS 3rd Edition, Section 7.4.1. Package Annotations).
* JAXBContext.newInstance( com.acme.foo.Foo.class )
The JAXBContext instance is intialized with class(es) passed as parameter(s) and classes that are statically reachable from these class(es). See newInstance(Class[]) for details.

virtualpt
Offline
Joined: 2008-02-25
Points: 0

OK, from some more hunting & digging, I've solved it :-)

I need to add the following annotation to my order class as follows, so the JAXBContext knows about it.

@XmlSeeAlso(TransitionalOrder.class)
public class Order {
//snip
}

see more detail here http://weblogs.java.net/blog/kohlert/archive/2006/10/jaxws_and_type.html

I hope this helps some of you out there :-)

Paul

gmolik
Offline
Joined: 2008-05-08
Points: 0

Your post has helped.

I'm having a similar issue, but I'm attempting to use java.util.Locale as a @WebParam. Everything works as long as Locale is a member of a parameter.

Have you been able to make the type adapter annotation work when things like Locale or Map are part of the formal parameters for a web method?

Thanks,
Greg

malahaas
Offline
Joined: 2009-11-18
Points: 0

Hi, I am running into a problem when using @XmlJavaTypeAdapter with @XmlSeeAlso.

I annotate the parent class with @XmlSeeAlso and @XmlJavaTypeAdapter and the subclass with its own @XmlJavaTypeAdapter. I've got both annotations to work separately (no type substitution with an adapter, and no adapter with type substitution) but not together. Were you able to use your object hierarchy in conjunction with adapters? It seems as though using @XmlSeeAlso causes JaxB to ignore or miss the @XmlJavaTypeAdapter which would eliminate the no-arg constructor error.

I get this error:
Caused by: javax.xml.ws.WebServiceException: Unable to create JAXBContext
at com.sun.xml.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:164)
at com.sun.xml.ws.model.AbstractSEIModelImpl.postProcess(AbstractSEIModelImpl.java:94)
at com.sun.xml.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:256)
at com.sun.xml.ws.server.EndpointFactory.createSEIModel(EndpointFactory.java:323)
at com.sun.xml.ws.server.EndpointFactory.createEndpoint(EndpointFactory.java:189)
at com.sun.xml.ws.api.server.WSEndpoint.create(WSEndpoint.java:467)
at org.jvnet.jax_ws_commons.spring.SpringService.getObject(SpringService.java:333)
at org.jvnet.jax_ws_commons.spring.SpringService.getObject(SpringService.java:45)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport$1.run(FactoryBeanRegistrySupport.java:121)
... 37 more
Caused by: java.security.PrivilegedActionException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 4 counts of IllegalAnnotationExceptions
com.ls.actuate.io.Source does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.Source
at public com.ls.actuate.io.Source com.ls.actuate.service.jaxws.Copy.arg0
at com.ls.actuate.service.jaxws.Copy
com.ls.actuate.io.SmbSource does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.SmbSource
at @javax.xml.bind.annotation.XmlSeeAlso(value=[class com.ls.actuate.io.SmbSource])
at public com.ls.actuate.io.Source com.ls.actuate.service.jaxws.Copy.arg0
at com.ls.actuate.service.jaxws.Copy
com.ls.actuate.io.Destination does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.Destination
at public com.ls.actuate.io.Destination com.ls.actuate.service.jaxws.Copy.arg1
at com.ls.actuate.service.jaxws.Copy
com.ls.actuate.io.SmbDestination does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.SmbDestination
at @javax.xml.bind.annotation.XmlSeeAlso(value=[class com.ls.actuate.io.SmbDestination])
at public com.ls.actuate.io.Destination com.ls.actuate.service.jaxws.Copy.arg1
at com.ls.actuate.service.jaxws.Copy

at java.security.AccessController.doPrivileged(Native Method)
at com.sun.xml.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:151)
... 45 more
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 4 counts of IllegalAnnotationExceptions
com.ls.actuate.io.Source does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.Source
at public com.ls.actuate.io.Source com.ls.actuate.service.jaxws.Copy.arg0
at com.ls.actuate.service.jaxws.Copy
com.ls.actuate.io.SmbSource does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.SmbSource
at @javax.xml.bind.annotation.XmlSeeAlso(value=[class com.ls.actuate.io.SmbSource])
at public com.ls.actuate.io.Source com.ls.actuate.service.jaxws.Copy.arg0
at com.ls.actuate.service.jaxws.Copy
com.ls.actuate.io.Destination does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.Destination
at public com.ls.actuate.io.Destination com.ls.actuate.service.jaxws.Copy.arg1
at com.ls.actuate.service.jaxws.Copy
com.ls.actuate.io.SmbDestination does not have a no-arg default constructor.
this problem is related to the following location:
at com.ls.actuate.io.SmbDestination
at @javax.xml.bind.annotation.XmlSeeAlso(value=[class com.ls.actuate.io.SmbDestination])
at public com.ls.actuate.io.Destination com.ls.actuate.service.jaxws.Copy.arg1
at com.ls.actuate.service.jaxws.Copy

at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:102)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:448)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:297)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:139)
at com.sun.xml.bind.api.JAXBRIContext.newInstance(JAXBRIContext.java:105)
at com.sun.xml.ws.developer.JAXBContextFactory$1.createJAXBContext(JAXBContextFactory.java:73)
at com.sun.xml.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:159)
at com.sun.xml.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:152)
... 47 more

malahaas
Offline
Joined: 2009-11-18
Points: 0

I've resolved the above error (I had the wrong subclass types in the generics for the Adapter class which apparently was causing it to ignore the adapter?). The problem I have now, is that subclasses (which are annotated with their own adapters) are still unmarshalled using the parent class's Adapter.

For instance, when I receive a SmbSourceRequest object that should be resolved to an SmbSource object, JaxB is using SourceAdapter as noted on the parent class Source instead of SmbSourceAdapter as noted on the subclass SmbSource.

Any thoughts on how to resolve this through the annotations? I would rather not have to check the class manually and use an adapter based on the actual Request class since that means any new subclasses created would then have to have another conditional added to SourceAdapter.

stua
Offline
Joined: 2010-01-06
Points: 0

I've had a similar issue handling classes like Locale. It seems that there's a useful annotation 'RequestWrapper' which can be applied to the method signature.

If you specify a javabean class via @RequestWrapper(name="my.request.Wrapper") then you can annotate the fields of that class with the appropriate @XmlJavaTypeAdapter. This works around wsgen ignoring anything I've specified in package-info.java when considering @WebParams.

I think that you might be able to use a custom RequestWrapper to add the appropriate annotations for your classes.