Getting Jersey 2.x POJO JSON support to work with

2020-07-22 09:57发布

问题:

I'm new RESTful web services and have been playing around with Jersey and Heroku (which uses a Jetty stack). I'm writing a simple REST API which returns a Map<String,String> in JSON for a GET request.

I'm however running into a 500 eror. The error message is :

HTTP Status 500 - org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class java.util.LinkedHashMap, genericType=java.util.HashMap.

Below is the code snippet for my resource :

@GET
@Produces("application/json")
public HashMap<String,String> getIt() {
    HashMap<String,String> nameValue = new LinkedHashMap<String,String>();
    nameValue.put("Alpha","One");
    return nameValue;
}

Below is my web.xml file:

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>org.example.services</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Here is my Main class

public class Main {

    public static void main(String[] args) throws Exception{
        // The port that we should run on can be set into an environment variable
        // Look for that variable and default to 8080 if it isn't there.
        String webPort = System.getenv("PORT");
        if (webPort == null || webPort.isEmpty()) {
            webPort = "8080";
        }

        final Server server = new Server(Integer.valueOf(webPort));
        final WebAppContext root = new WebAppContext();

        root.setContextPath("/");
        // Parent loader priority is a class loader setting that Jetty accepts.
        // By default Jetty will behave like most web containers in that it will
        // allow your application to replace non-server libraries that are part of the
        // container. Setting parent loader priority to true changes this behavior.
        // Read more here: http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
        root.setParentLoaderPriority(true);

        final String webappDirLocation = "src/main/webapp/";
        root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
        root.setResourceBase(webappDirLocation);


        server.setHandler(root);

        server.start();
        server.join();
    }
}

Even after browsing through previous Stackoverflow answers like this or this, I could not find a way to solve my problem as they do not address Jersey 2.x with Jetty. I've added the following to my pom.xml file, however the problem still persists as unable to register the JSON bindings with the Jetty server.

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
</dependency>

回答1:

Automatic registration of providers by moxy didn't work as stated by Jersey Reference.

As per what they have stated, only moxy and jackson has POJO to JSON conversion feature.

Documentation says Jackson doesn't auto register(Not a problem any way!)

1. Swap Moxy with Jackson in POM.XML

Remove :

<dependency>
 <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-moxy</artifactId>
  <version>${jersey.version}</version>
</dependency>

Add:

<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-json-jackson</artifactId>
  <version>${jersey.version}</version>
</dependency>

2. Register Jackson Message Body Readers and Writers :

Add org.codehaus.jackson.jaxrs to provider packages list. Here is my web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
    <servlet-name>Web app name</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.myorg.myproj.api;org.codehaus.jackson.jaxrs</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Web app name</servlet-name>
    <url-pattern>/v1/*</url-pattern>
</servlet-mapping>


P.S.
I am not promoting jackson, just that moxy didn't work for me, its writers failed to auto register as they advertised and could not find documentation about manual registration!



回答2:

If using Jackson Implementation

As per the Jersey Documentation - Note that there is a difference in namespaces between Jackson 1.x (org.codehaus.jackson) and Jackson 2.x (com.fasterxml.jackson).

If you are using jackson 2.x, you need to register com.fasterxml.jackson.jaxrs as init param to the ServletContainer in your web.xml as follows:

<servlet>
    <servlet-name>RESTServlet</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.fasterxml.jackson.jaxrs</param-value>
    </init-param>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>