J'essaie de transmettre un objet via le service Web REST. Voici mes cours explique la fonctionnalité dont j'ai besoin à l'aide de quelques exemples de codes.
Méthode de la classe de service Web de repos
@POST
@Path("/find")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces({MediaType.APPLICATION_JSON})
public Response getDepartments(){
Response response = new Response();
try {
response.setCode(MessageCode.SUCCESS);
response.setMessage("Department Names");
Department dept = new Department("12", "Financial");
response.setPayload(dept);
} catch (Exception e) {
response.setCode(MessageCode.ERROR);
response.setMessage(e.getMessage());
e.printStackTrace();
}
return response;
}
Classe de réponse
import Java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Response implements Serializable{
private static final long serialVersionUID = 1L;
public enum MessageCode {
SUCCESS, ERROR, UNKNOWN
}
private MessageCode code;
private String message;
private Object payload;
public MessageCode getCode() {
return code;
}
public void setCode(MessageCode code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getPayload() {
return payload;
}
public void setPayload(Object payload) {
this.payload = payload;
}
}
Classe de département
@XmlRootElement
public class Department implements Java.io.Serializable {
private String deptNo;
private String deptName;
public Department() {
}
public Department(String deptNo, String deptName) {
this.deptNo = deptNo;
this.deptName = deptName;
}
public String getDeptNo() {
return this.deptNo;
}
public void setDeptNo(String deptNo) {
this.deptNo = deptNo;
}
public String getDeptName() {
return this.deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
}
Lorsque j'appelle la méthode getDepartments dans la classe de service Web de repos, elle renvoie les exceptions suivantes. Mais si je change le type Object du payload en Department dans la classe Response, il renvoie correctement la réponse json. Mais comme je dois utiliser cette classe Response pour différents types de classes, je ne peux pas limiter la charge utile à un type de classe. Quelqu'un peut-il m'aider s'il vous plaît dans cette affaire?
Trace de la pile
Dec 27, 2012 9:34:18 PM com.Sun.jersey.spi.container.ContainerResponse logException
SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException: javax.xml.bind.MarshalException
- with linked exception:
[javax.xml.bind.JAXBException: class Department nor any of its super class is known to this context.]
at com.Sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.Java:159)
at com.Sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.Java:306)
at com.Sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.Java:1437)
at com.Sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.Java:1349)
at com.Sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.Java:1339)
at com.Sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.Java:416)
at com.Sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.Java:537)
at com.Sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.Java:699)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.Java:511)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.Java:401)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.Java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.Java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.Java:766)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.Java:450)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.Java:152)
at org.mortbay.jetty.Server.handle(Server.Java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.Java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.Java:945)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.Java:756)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.Java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.Java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.Java:410)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.Java:582)
Caused by: javax.xml.bind.MarshalException
- with linked exception:
[javax.xml.bind.JAXBException: class Department nor any of its super class is known to this context.]
at com.Sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.Java:323)
at com.Sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.Java:177)
at com.Sun.jersey.json.impl.BaseJSONMarshaller.marshallToJSON(BaseJSONMarshaller.Java:103)
at com.Sun.jersey.json.impl.provider.entity.JSONRootElementProvider.writeTo(JSONRootElementProvider.Java:136)
at com.Sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.Java:157)
... 23 more
Caused by: javax.xml.bind.JAXBException: class Department nor any of its super class is known to this context.
at com.Sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.Java:250)
at com.Sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.Java:265)
at com.Sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.Java:657)
at com.Sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.Java:156)
at com.Sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.Java:344)
at com.Sun.xml.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.Java:597)
at com.Sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.Java:328)
at com.Sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.Java:498)
at com.Sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.Java:320)
... 27 more
Caused by: javax.xml.bind.JAXBException: class Department nor any of its super class is known to this context.
at com.Sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.Java:611)
at com.Sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.Java:652)
... 33 more
Les implémentations JAX-RS prennent automatiquement en charge le marshalling/unmarshalling de classes basées sur des annotations JAXB détectables, mais comme votre charge utile est déclarée comme étant Object
, je pense que la JAXBContext
créée manque la classe Department
et quand il est temps de la gérer, elle ne sait pas comment.
Une solution rapide et incorrecte consisterait à ajouter une annotation XmlSeeAlso
à votre classe de réponse:
@XmlRootElement
@XmlSeeAlso({Department.class})
public class Response implements Serializable {
....
ou quelque chose d'un peu plus compliqué serait "d'enrichir" le contexte JAXB pour la classe Response
en utilisant un ContextResolver
:
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
@Provider
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class ResponseResolver implements ContextResolver<JAXBContext> {
private JAXBContext ctx;
public ResponseResolver() {
try {
this.ctx = JAXBContext.newInstance(
Response.class,
Department.class
);
} catch (JAXBException ex) {
throw new RuntimeException(ex);
}
}
public JAXBContext getContext(Class<?> type) {
return (type.equals(Response.class) ? ctx : null);
}
}
J'ai le même problème et je l'ai résolu en ajoutant un paquet à explorer dans le Jaxb2marshaller. Pour le printemps sera défini un haricot comme ceci:
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
String[] packagesToScan= {"<packcge which contain the department class>"};
marshaller.setPackagesToScan(packagesToScan);
return marshaller;
}
Ainsi, si toutes vos classes de demande et de réponse se trouvent dans le même package, vous n'avez pas besoin d'indiquer spécifiquement les classes sur JAXBcontext.
Cette exception peut être résolue en spécifiant un chemin de classe complet.
Exemple:
Si vous utilisez une classe nommée ExceptionDetails
Mauvaise façon de passer des arguments
JAXBContext jaxbContext = JAXBContext.newInstance(ExceptionDetails.class);
Droit de passer des arguments
JAXBContext jaxbContext = JAXBContext.newInstance(com.tibco.schemas.exception.ExceptionDetails.class);
J'ai eu un problème similaire en utilisant l'implémentation de référence JAXB et JBoss AS 7.1. J'ai pu écrire un test d'intégration confirmant que JAXB fonctionnait en dehors de l'environnement JBoss (suggérant que le problème pourrait provenir du chargeur de classes dans JBoss).
C'est le code qui donnait l'erreur (c'est-à-dire ne fonctionnant pas):
private static final JAXBContext JC;
static {
try {
JC = JAXBContext.newInstance("org.foo.bar");
} catch (Exception exp) {
throw new RuntimeException(exp);
}
}
et c'est le code qui a fonctionné (ValueSet est l'une des classes marshalées à partir de mon XML).
private static final JAXBContext JC;
static {
try {
ClassLoader classLoader = ValueSet.class.getClassLoader();
JC = JAXBContext.newInstance("org.foo.bar", classLoader);
} catch (Exception exp) {
throw new RuntimeException(exp);
}
}
Dans certains cas, j'ai eu la classe, aucune de ses super classes n'est connue dans ce contexte. Dans d'autres cas, une exception d'org.foo.bar.ValueSet ne peut pas être transtypée en org.foo.bar.ValueSet (problème similaire à celui décrit ici: ClassCastException lors de la conversion vers la même classe ).
J'avais fait face à une erreur similaire en prenant en charge une application. Il s'agissait des classes générées pour un service Web SOAP.
Le problème était dû aux classes manquantes. Lorsque javax.xml.bind.Marshaller essayait de rassembler l'objet jaxb, il ne trouvait pas toutes les classes dépendantes après quoi il était généré par wsdl et xsd. après avoir ajouté le fichier jar avec toutes les classes du chemin de classe, le problème a été résolu.
Je sais que c'est une vieille question, mais vous pouvez modifier la réponse à l'aide d'un paramètre (P):
public class Response<P> implements Serializable{
private static final long serialVersionUID = 1L;
public enum MessageCode {
SUCCESS, ERROR, UNKNOWN
}
private MessageCode code;
private String message;
private P payload;
...
public P getPayload() {
return payload;
}
public void setPayload(P payload) {
this.payload = payload;
}
}
La méthode serait
public Response<Departments> getDepartments(){...}
Je ne peux pas l'essayer maintenant mais ça devrait marcher.
Sinon, il est possible d'étendre la réponse
@XmlRootElement
public class DepResponse extends Response<Department> {<no content>}
La réponse de Ftrujillo fonctionne bien, mais si vous n'avez qu'un seul paquet à analyser, c'est la forme la plus courte ::
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("your.package.to.scan");
return marshaller;
}