web-dev-qa-db-fra.com

JAX WS RS avec Jersey - Collection de renvoi, carte, etc.

J'utilise Jersey 2.3

Ma classe de point de terminaison WS:

@Path("/employees")
public class EmployeeWSEndpoint {

    @Context
    Request request;

    @GET
    @Path("/allEmployees")
    @Produces(MediaType.APPLICATION_JSON)
    public List<Employee> getAllEmployees() {

        System.out.println("In EmployeeWSEndpoint.getAllEmployees()");

        EmployeeService employeeService = new EmployeeServiceImpl();
        return employeeService.getAllEmployees();
    }

    @GET
    @Path("/allDept")
    @Produces(MediaType.APPLICATION_JSON)
    public List<String> getAllDept() {

        System.out.println("In EmployeeWSEndpoint.getAllDept()");

        List<String> deptNames = new ArrayList<>();
        deptNames.add("CoE");
        deptNames.add("iLabs");

        return deptNames;
    }

    @GET
    @Path("/allDeptPOJO")
    @Produces(MediaType.APPLICATION_JSON)
    public TempPOJO getAllDeptPOJO() {

        System.out.println("In EmployeeWSEndpoint.getAllDeptPOJO");

        TempPOJO tempPOJO = new TempPOJO();

        return tempPOJO;
    }
}

POJO employé:

public class Employee {

    private int id;
    private String firstName;
    private String middleName;
    private String lastName;
    private Date dob;
    private int deptId;

    public Employee() {

    }

    public Employee(String firstName, String middleName, String lastName,
            Date dob, int deptId) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.dob = dob;
        this.deptId = deptId;
    }

    public Employee(int id, String firstName, String middleName,
            String lastName, Date dob, int deptId) {
        this.id = id;
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.dob = dob;
        this.deptId = deptId;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

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

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getLastName() {
        return lastName;
    }

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

    public Date getDob() {
        return dob;
    }

    public void setDob(Date dob) {
        this.dob = dob;
    }

    public int getDeptId() {
        return deptId;
    }

    public void setDeptId(int deptId) {
        this.deptId = deptId;
    }

    @Override
    public String toString() {
        return firstName + " " + middleName + " " + " " + lastName;
    }
}

Un POJO temporaire:

public class TempPOJO {

    private List<String> deptNames;

    public List<String> getDeptNames() {

        deptNames = new ArrayList<>();
        deptNames.add("CoE");
        deptNames.add("iLabs");

        return deptNames;
    }

    public void setDeptNames(List<String> deptNames) {
        this.deptNames = deptNames;
    }

}

Lister le ou les résultats pour différentes méthodes:

tous les employés

[{"deptId":1,"dob":"1978-06-19","firstName":"Garfield","id":0,"lastName":"Arbuckle","middleName":"John"},{"deptId":1,"dob":"1946-07-07","firstName":"John","id":0,"lastName":"Rambo","middleName":"R"}]

/allDept

In EmployeeWSEndpoint.getAllDept()
Oct 01, 2013 3:31:08 PM org.glassfish.jersey.server.ServerRuntime$Responder mapException
WARNING: WebApplicationException cause:
javax.xml.bind.MarshalException
 - with linked exception:
[Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.Eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: Exception [EclipseLink-25007] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.Eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor for class Java.lang.String was not found in the project.  For JAXB, if the JAXBContext was bootstrapped using TypeMappingInfo[] you must call a marshal method that accepts TypeMappingInfo as an input parameter.]
    at org.Eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.Java:403)
    at org.Eclipse.persistence.jaxb.rs.MOXyJsonProvider.writeTo(MOXyJsonProvider.Java:808)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.Java:243)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.Java:230)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.Java:149)
    at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.Java:103)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.Java:149)
    at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.Java:88)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.Java:149)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.Java:1139)
    at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.Java:562)
    at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.Java:357)
    at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.Java:347)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.Java:258)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.Java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.Java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.Java:318)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.Java:235)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.Java:983)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.Java:359)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.Java:372)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.Java:335)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.Java:218)
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:305)
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:210)
    at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:222)
    at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:123)
    at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:472)
    at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:171)
    at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:99)
    at org.Apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.Java:947)
    at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:118)
    at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:408)
    at org.Apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.Java:1009)
    at org.Apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.Java:589)
    at org.Apache.Tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.Java:310)
    at Java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at Java.lang.Thread.run(Unknown Source)
Caused by: Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.Eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: Exception [EclipseLink-25007] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.Eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor for class Java.lang.String was not found in the project.  For JAXB, if the JAXBContext was bootstrapped using TypeMappingInfo[] you must call a marshal method that accepts TypeMappingInfo as an input parameter.
    at org.Eclipse.persistence.exceptions.XMLMarshalException.marshalException(XMLMarshalException.Java:97)
    at org.Eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.Java:911)
    at org.Eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.Java:848)
    at org.Eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.Java:401)
    ... 41 more
Caused by: Exception [EclipseLink-25007] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.Eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor for class Java.lang.String was not found in the project.  For JAXB, if the JAXBContext was bootstrapped using TypeMappingInfo[] you must call a marshal method that accepts TypeMappingInfo as an input parameter.
    at org.Eclipse.persistence.exceptions.XMLMarshalException.descriptorNotFoundInProject(XMLMarshalException.Java:139)
    at org.Eclipse.persistence.internal.oxm.Context$ContextState.getSession(Context.Java:143)
    at org.Eclipse.persistence.oxm.XMLContext$XMLContextState.getSession(XMLContext.Java:787)
    at org.Eclipse.persistence.oxm.XMLContext$XMLContextState.getSession(XMLContext.Java:1)
    at org.Eclipse.persistence.internal.oxm.Context.getSession(Context.Java:451)
    at org.Eclipse.persistence.oxm.XMLContext.getSession(XMLContext.Java:356)
    at org.Eclipse.persistence.oxm.XMLContext.getSession(XMLContext.Java:1)
    at org.Eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.Java:568)
    at org.Eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.Java:1096)
    at org.Eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.Java:869)
    ... 43 more

/allDeptPOJO

{"deptNames":["CoE","iLabs"]}

J'ai quelques requêtes :

  1. Je suppose que depuis Jersey 2.3, l'annotation 'XmlRootElement' n'est pas nécessaire pour les POJO - je ne l'ai pas encore fait, les appels fonctionnent. Veuillez confirmer
  2. Dans le cas d'une liste d'un pojo (employé) ou d'un pojo ayant une liste, la réponse est correcte, mais pour Liste, Carte, etc., elle échoue (essentiellement, pour les types Java). Si cela fonctionne pour Employee, pourquoi ne fonctionne-t-il pas pour les types Java? Est-il nécessaire d'écrire un XmlAdapter? 

Note : Je veux la réponse en JSON dans la plupart des cas.

Modification du fichier web.xml pour utiliser Jackson:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://Java.Sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://Java.Sun.com/xml/ns/javaee http://Java.Sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>Jersey</display-name>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>Jersey REST Service</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.ws.jaxrs.jersey</param-value>
        </init-param>
        <init-param>
            <param-name>jersey.config.disableMoxyJson.server</param-name>
            <param-value>true</param-value>
        </init-param>

        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.ws.jaxrs.jersey;org.codehaus.jackson.jaxrs</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey REST Service</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>


</web-app>

J'ai eu l'exception suivante:

In EmployeeWSEndpoint.getAllDept()
Nov 07, 2013 10:04:12 AM org.Apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [Jersey REST Service] in context with path [/Jersey] threw exception [org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class Java.util.ArrayList, genericType=Java.util.List<Java.lang.String>.] with root cause
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class Java.util.ArrayList, genericType=Java.util.List<Java.lang.String>.
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.Java:227)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.Java:149)
    at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.Java:103)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.Java:149)
    at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.Java:88)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.Java:149)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.Java:1139)
    at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.Java:562)
    at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.Java:357)
    at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.Java:347)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.Java:258)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.Java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.Java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.Java:318)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.Java:235)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.Java:983)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.Java:359)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.Java:372)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.Java:335)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.Java:218)
    at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:304)
    at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:210)
    at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:224)
    at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:169)
    at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:472)
    at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:168)
    at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:100)
    at org.Apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.Java:929)
    at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:118)
    at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:405)
    at org.Apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.Java:964)
    at org.Apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.Java:515)
    at org.Apache.Tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.Java:302)
    at Java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at Java.lang.Thread.run(Unknown Source)
14

Jackson est la meilleure solution pour moi pour le moment.

Pour utiliser Jackson en tant que fournisseur JSON, vous devez ajouter le module jersey-media-json-jackson à votre fichier pom.xml:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.7</version>
</dependency>

Remarque: La fonction de découverte automatique ne fonctionne pas.

Création du client avec la fonctionnalité Jackson JSON activée:

final Client client = ClientBuilder.newBuilder()
                    .register(JacksonFeature.class)
                    .build();

Création d'une application serveur JAX-RS avec la fonctionnalité Jackson JSON activée:

final Application application = new ResourceConfig()
                              .packages("my.rest.package")
                              .register(JacksonFeature.class);

Consultez également l'exemple officiel Jersey json-jackson .

16

Utiliser un autre fournisseur JSON

Je ne sais pas pourquoi jersey startet a adopté MOXy comme fournisseur JSON par défaut, mais c'est évidemment une mauvaise idée.

Les problèmes que vous (et moi-même - ainsi que d’autres) rencontrons sont le résultat de MOXy beeing incapable de mapper des collections de types Java simples (comme String) - c’est ridicule à mon humble avis. Les autres fournisseurs (comme jackson ) n'ont pas ce problème, donc le changement aidera certainement.

Voici ce que j'ai fait:

  1. Indiquez à Jersey not d'utiliser MOXy. Ajoutez ceci à votre web.xml:

    <init-param>
        <param-name>jersey.config.disableMoxyJson.server</param-name>
        <param-value>true</param-value>
    </init-param>
    
  2. Ajoutez Jackson à votre pom. Quelque chose comme ça va faire:

    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-jaxrs</artifactId>
        <version>1.9.12</version>
    </dependency>
    
  3. Dites à Jersey de scanner les paquets de Jackson Jackson Il trouvera (et utilisera) les fournisseurs JSON fournis par Jackson:

    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>com.example.your.package;org.codehaus.jackson.jaxrs</param-value>
    </init-param>
    
7
sne11ius

Remarque: Je suis le EclipseLink JAXB (MOXy) lead et membre du JAXB (JSR-222) expert groupe.

L'exception que vous frappez est due à un bogue dans MOXyJsonProvider de MOXy. MOXyJsonProvider est une implémentation des interfaces JAX-RS MessageBodyReader/MessageBodyWriter. J'ai changé la méthode isWriteable pour renvoyer false pour List<String> pour renvoyer false afin qu'un autre fournisseur puisse la gérer.

J'ai également ouvert la demande d'amélioration suivante pour que cette prise en charge soit ajoutée à MOXy:

6
Blaise Doughan

Vous pouvez utiliser RestEasy comme implémentation JAX-RS

GlassFish4 utilise Jersey/MOXy comme implémentation de JAX-RS. Ils ont des problèmes de mappage des collections de types Java simples (comme String) vers JSON - https://bugs.Eclipse.org/bugs/show_bug .cgi? id = 417723

WildFly et JBoss utilisent RestEasy/Jackson comme implémentation de JAX-RS et la cartographie ne pose aucun problème.

Vous pouvez utiliser RestEasy au lieu de Jersey dans n’importe quel serveur d’applications Java, par exemple. WildFly (JBoss), GlassFish ou Tomcat, 

remplacez simplement:

    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>7.0</version>
        <scope>provided</scope>
    </dependency>

avec:

    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-servlet-initializer</artifactId>
        <version>3.0.7.Final</version>
        <scope>compile</scope>
    </dependency>        
    <dependency>
       <groupId>org.jboss.resteasy</groupId>
       <artifactId>resteasy-jaxrs</artifactId>
       <version>3.0.7.Final</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jackson-provider</artifactId>
        <version>3.0.7.Final</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jaxb-provider</artifactId>
        <version>3.0.7.Final</version>
        <scope>compile</scope>
    </dependency>

Cela fonctionne pour moi.

Meilleures salutations de Lublin en Pologne :)

2
wildloop

J'ai eu le même problème en renvoyant un objet List<String>. Je l'ai enveloppé dans une classe RestStringList et cela fonctionne. Si nous en avons plus à faire, je pourrais passer à Jackson.

import Java.io.Serializable;
import Java.util.ArrayList;
import Java.util.List;

public class RestStringList implements Serializable {
    private static final long serialVersionUID = 1L;

    private List<String> stringList = null;

    public RestStringList() {
        this.stringList = new ArrayList<String>();
    }

    public void setStringList(List<String> stringList) {
        this.stringList = new ArrayList<String>(stringList);
    }

    public List<String> getStringList() {
        return stringList;
    }

    public String toString() {
        return "{ strings=" + stringList + " }";
    }
}
0
Charissa Willard