kayasa
kayasa

Reputation: 2085

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

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?

Upvotes: 7

Views: 20329

Answers (6)

Carlo GS
Carlo GS

Reputation: 1

The real problem is that you need to put your List inside a class, try this:

public class UserListClass() {
  private List<User> userList = new ArrayList<User>();

  public List<User> getUserList() { return userList; }
  public setUserList(List<User> userList) { this.userList = userList; }
}

and your code like this:

@Path("/UserService")
public class UserService {

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

Upvotes: 0

nicKarahtar
nicKarahtar

Reputation: 59

I tried a ton of these dependencies but none of them worked for my version of Jersey3. What I needed to do was turn the Arraylist into an actual array. I managed this with toArray() and it started serializing correctly!

Upvotes: 1

padippist
padippist

Reputation: 1216

Your xml was working so I assume that you have @XmlRootElement annotation in your User class.

The thing is, it knows how to convert it to xml with the annotation @XmlRootElement but it doesn't know how to convert it to JSON.

So for making it convert everything to JSON with the same annotation of xml(ie @XmlRootElement) we can add

jersey-media-moxy-<whatever version>.jar

or for maven users

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

Also it should have a no argument constructor

Upvotes: 2

cassiomolin
cassiomolin

Reputation: 131117

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.

Upvotes: 14

Werner Daehn
Werner Daehn

Reputation: 635

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.

Upvotes: 3

Marcinek
Marcinek

Reputation: 2117

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>

Upvotes: 2

Related Questions