Need a way to generate JSON from List<map<string,string> in JAX RS service. jackson2

时间:2015-07-28 16:25:10

标签: java json maven jax-rs

I have the following in my POM.xml Updated complete pom:

<dependencies>
                <dependency>
                <groupId>org.glassfish.jersey.containers</groupId>
                <artifactId>jersey-container-servlet</artifactId>
                <version>2.19</version>
            </dependency>
            <dependency>
                <groupId>org.glassfish.jersey.media</groupId>
                <artifactId>jersey-media-json-jackson</artifactId>
                <version>2.19</version>
            </dependency>
            <!-- <dependency>
                <groupId>org.glassfish.jersey.media</groupId>
                <artifactId>jersey-media-moxy</artifactId>
                <version>2.19</version>
            </dependency> -->



            <dependency>
                <groupId>org.mvel</groupId>
                <artifactId>mvel2</artifactId>
                <version>2.2.1.Final</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.0</version>
            </dependency>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.2.4</version>
            </dependency>

            <dependency>
                <groupId>org.apache.drill.exec</groupId>
                <artifactId>drill-jdbc-all</artifactId>
                <version>1.1.0</version>
            </dependency>


        </dependencies>

I wanted to generate an JSON from List<Map<String,String>>. I always end up having MessageBodyWriter not found for media type=application/json, type=class java.util.LinkedHashMap, genericType=jav a.util.Map<String,String>

I even tried with simple map

@GET
      @Path("/test")
      @Produces(MediaType.APPLICATION_JSON)
      public Map<String, String> getMap() {
          Map<String, String> map = new HashMap<String, String>();
          map.put("some key", "some value");
          return map;
      }

even this produces the same error message.

UPDATED EXCEPTION STACK TRACE:

Jul 29, 2015 9:23:29 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [CustomerDataService] in context with path [/CustomerDataService] threw exception 
[org.glassfish.jersey.server.ContainerException: java.lang.AbstractMethodError: com.fasterxml.jackson.jaxrs.json.Jackson
JaxbJsonProvider.isWriteable(Ljava/lang/Class;Ljava/lang/reflect/Type;[Ljava/lang/annotation/Annotation;Ljavax/ws/rs/cor
e/MediaType;)Z] with root cause
java.lang.AbstractMethodError: com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.isWriteable(Ljava/lang/Class;Lja
va/lang/reflect/Type;[Ljava/lang/annotation/Annotation;Ljavax/ws/rs/core/MediaType;)Z
    at org.glassfish.jersey.message.internal.MessageBodyFactory.isWriteable(MessageBodyFactory.java:1158)
    at org.glassfish.jersey.message.WriterModel.isWriteable(WriterModel.java:86)
    at org.glassfish.jersey.message.internal.MessageBodyFactory._getMessageBodyWriter(MessageBodyFactory.java:798)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.getMessageBodyWriter(MessageBodyFactory.java:756)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterc
eptorExecutor.java:241)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:106)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInter
ceptor.java:86)

2 个答案:

答案 0 :(得分:0)

You cannot serialize collections and maps. Create a container class.

For example:

@XmlRootElement
public class Container {
    public Map<String, String> data;
}

public Container getMap() {
      Map<String, String> map = new HashMap<String, String>();
      map.put("some key", "some value");

      // wrap content int container object
      Container container = new Container();
      container.data = map;
      return container ;
}

The reason for this issue is, that XML always has to have a single root element.

答案 1 :(得分:0)

MOXy doesn't handle maps well as it uses JAXB under the hood, and JAXB sucks with Maps. Use Jackson instead. Get rid of the MOXy dependency. MOXy is the default, so if it is on the classpath, it will be used over all others. Once you take it out, Jackson will be used.


UPDATE

So the drill-jdbc-all actually repackages both Jackson 1.x and Jackson 2.x. You can exclude them from the jersey-media-json-jackson depedency, since drill already includes them.

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.19</version>
    <exclusions>
        <exclusion>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-base</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-json-provider</artifactId>
        </exclusion> 
        <exclusion>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </exclusion>
    </exclusions>
</dependency>

UPDATE 2

I guess the above still doesn't work. One way I was able to get to work is adding jackson-jaxrs-json-provider (you also need to take out jersey-media-json-jackson and the jersey-media-moxy).

<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.drill.exec</groupId>
    <artifactId>drill-jdbc-all</artifactId>
    <version>1.1.0</version>
</dependency>

The key to this is that it must be declared before the apache-jdbc-drill. Yea it seems more of a work around that a solution. But the fact that drill packages the dependency is a problem. When Jersey scans, it will will add ours first, and ignore drill's.

You then need to add the provider to your Jersey config

<init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>
        com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
    </param-value>
</init-param>

I mean I haven't tested this 10,000, but for the 10 to 20 times I did tested, the ordering of the dependencies was the deciding factor for whether or not it would work. Again, like I said, seems like a work-around.

And the add it to the Jersey config