MessageBodyWriter not found for media type=application/json when returning JSON in REST web service with Jersey

16,481

Solution 1

To use Jackson 2.x as your JSON provider you need to add jersey-media-json-jackson module to your pom.xml file:

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

And then register the JacksonFeature in your Application/ResourceConfig subclass.

For more details, have a look at Jersey documentation.

Solution 2

You need a json serializer on your class path to make this work.

Just add jackson and jersey will use this in the writer. E.g. if you are using maven, add this to the pom.xml

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.7.4</version>
</dependency>

Solution 3

I am a bit upset about JAXB binding as well at the moment, therefore let me summarize my findings here - please correct me if I say something stupid:

  1. Of course you have to have a library to do the JSON (de)serialization, in my case it is Moxy.
  2. You have to tell JAXB which classes it should support. There are multiple ways to do that, the simplest seems to be to add a jaxb.properties file in the directory matching your classes and its sole content is the text javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory. With the directory I mean, if your classes are in the directory src/main/java and there the package com.pkg1.entities, add this file in src/main/resources and there as com/pkg1/entities/jaxb.properties.
  3. By default JAXB works on POJOs. So you need to have a constructor without arguments, a get- and a set-method. Only then this field will be present in the JSON.
  4. What I do often is to add a second constructor that gets the runtime object passed in and sets all fields to be exposed directly. Hence I do not need and do not want a set-method. Solution is to annotate the get method with @XmlElement.
  5. Did I say you need an empty/default constructor? Took me once three hours to find out why class1 was working fine, class2 got the MessageBodyWriter error. I had forgotten the constructor. Grrrrr.
  6. You get the same error (I believe) when the class is configured fine but one of its fields returns a type it cannot serialize.
  7. I believe to have had one case where the class annotation @XmlRootElement caused that error. Not sure but I barely use that annotation at the moment.
  8. In case you have a List as one of the elements to be turned into a Json array, JAXB will use the myAbstract class to do the serialization. Not very useful, you want the actual objects to be serialized. But how should JAXB know who implemented/extended this class? You have to tell with the annotation @XmlSeeAlso. So the MyAbstract class gets a class annotation @XmlSeeAlso({MyConcrete1.class, MyConcrete2.class}). At least Moxy does add then an additional type field telling the consumer which class it was. Makes total sense.
  9. Although you can return userList the better option would be to return Response.ok().entity(userList).build(); Then you can return errors as well. Logically it is the same.
  10. Watchout what kind of data types you use. String is fine, ArrayList as well, Hashtable not. Depends on the serializer you use as well.

I hope this helps others.

Share:
16,481
kayasa
Author by

kayasa

Updated on August 01, 2022

Comments

  • kayasa
    kayasa almost 2 years

    I am trying to create a very simple REST service using Jersey. Here is the service code

    @Path("/UserService")
    public class UserService {
    
        @GET
        @Path("/users")
        @Produces(MediaType.APPLICATION_XML)
        public List<User> getUsers() {
            User user = new User(1, "Thomas", "Greene");
            List<User> userList = new ArrayList<User>();
            userList.add(user);
            return userList;
        }
    }
    

    When I run it through Postman, it returns me a XML response

    XML response in Postman

    Now, I want to get a JSON response back. So, I changed the mediatype to application/json:

    @Path("/UserService")
    public class UserService {
    
        @GET
        @Path("/users")
        @Produces(MediaType.APPLICATION_JSON)
        public List<User> getUsers(){ 
            User user = new User(1, "Thomas", "Greene");
            List<User> userList = new ArrayList<User>();
            userList.add(user);
            return userList;
       }    
    }
    

    It gives me the below error in Tomcat logs:

    SEVERE: MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List.

    Can someone please guide me how to get a JSON response back?

  • kayasa
    kayasa almost 8 years
    Than you. I added 'jackson-2.1.0-all.jar' to the WEB-INF/lib but that did not help.
  • kayasa
    kayasa almost 8 years
    I am not using maven. I have just created a 'Dynamic Web project' using eclispe. This gave me a WEB-INF/lib directory. I added 'com.fasterxml.jackson.core.jar', 'com.fasterxml.jackson.databind.jar' and 'jackson-annotations-2.1.2.jar' to the lib directory. Finally, I just export this project as a war and deploy in Tomcat. So, I neither have pom.xml nor can I locate Application/ResourceConfig. Wouldn't adding these 3 jars to WEB-INF/lib suffice?
  • cassiomolin
    cassiomolin almost 8 years
    @kayasa You can add the dependencies manually (have a look at the dependencies will need here). But I do recommend you to use Maven nowadays. Seriously, there's no good reason to manage your dependencies manually. To convert your existing project to a Maven project, have a look here.
  • kayasa
    kayasa almost 8 years
    I also tried removing 'jackson-2.1.0-all.jar' and instead added the below 3 jars to WEB-INF/lib - com.fasterxml.jackson.core.jar - com.fasterxml.jackson.databind.jar - jackson-annotations-2.1.2.jar I don't have pom.xml as I a not using maven. I was hoping that adding these three jars to WEB-INF/lib to solve the problem but it didn't. Does it require any code changes as well?
  • kayasa
    kayasa almost 8 years
    Thanks for the recommendation. I agree it's a good idea to use Maven. However, I am new to both Jersey and Maven so wanted to do one thing at a time. I would prefer to get a 'Hello World' REST example working before picking up Maven.
  • cassiomolin
    cassiomolin almost 8 years
    @kayasa Fist things first. Maven will save your time ;) For more details on the dependencies, check the Jersey documentation.
  • Andy Dufresne
    Andy Dufresne almost 7 years
    I tried this. Though adding jackson-core dependency didn't help to resolve the error, I would like to understand the internals of how would jackson jar resolve the error.
  • Andrejs
    Andrejs over 6 years
    Note to anyone: how to register the JacksonFeature -> stackoverflow.com/questions/18317927/…