37

I have wrote a JAX-RS server and client both use Jersey. I want to sent a collection of my entities to client and I made this steps:

  1. Made entity extends Serializable
  2. Wrote a custom provider and extended it to support a collections
  3. Copy-paste entity and provider to client side

I make a request, it sucessfully handled on the server side by client receives an error:

org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=interface java.util.List, genericType=java.util.List<model.HotelsEntity>.
org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:225)
org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:149)
org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:853)
org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:812)
org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:377)
org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:813)
org.glassfish.jersey.client.JerseyInvocation.access$600(JerseyInvocation.java:90)
org.glassfish.jersey.client.JerseyInvocation$3.call(JerseyInvocation.java:693)
org.glassfish.jersey.internal.Errors.process(Errors.java:315)
org.glassfish.jersey.internal.Errors.process(Errors.java:297)
org.glassfish.jersey.internal.Errors.process(Errors.java:228)
org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:424)
org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:689)
org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:405)
org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:301)
service.HotelService.getHotels(HotelService.java:30)
actions.HotelAction.perform(HotelAction.java:42)
MainServlet.processResponse(MainServlet.java:33)
MainServlet.doPost(MainServlet.java:22)
javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

Server:

    @GET
@Produces(MediaType.APPLICATION_JSON)
public Response getHotelsList(@QueryParam("startDate") String startDate,
                              @QueryParam("endDate") String endDate) {
    List<HotelsEntity> list = hotelService.getAll();
    return ResponseFactory.response(Response.Status.OK, list);
}

Client:

    GenericType<List<HotelsEntity>> genericType = new GenericType<List<HotelsEntity>>(){};
    WebTarget target = client.target(preparePath());
    List<HotelsEntity> hotels = target.request(MediaType.APPLICATION_JSON_TYPE).get(genericType);

Provider:

public class JsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {

@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return MediaType.APPLICATION_JSON.equals(mediaType.getType()) &&
            MediaType.APPLICATION_JSON.equals(mediaType.getSubtype());
}

@Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
    Gson gson = createGson();
    Reader reader = new InputStreamReader(entityStream, Charset.forName(Constants.UTF_8));
    return gson.fromJson(reader, genericType);
}

@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return MediaType.APPLICATION_JSON.equals(mediaType.getType()) &&
            MediaType.APPLICATION_JSON.equals(mediaType.getSubtype());
}

@Override
public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return -1;
}

@Override
public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
    Gson gson = createGson();
    JsonElement element = gson.toJsonTree(entityStream);
    Writer writer = null;
    try {
        writer = new OutputStreamWriter(entityStream, Charset.forName(Constants.UTF_8));
        gson.toJson(element, writer);
    } finally {
        if (writer != null) {
            writer.flush();
        }
    }
}

private Gson createGson() {
    return new GsonBuilder().setPrettyPrinting().create();
}

}

@Provider
public class JsonCollection extends JsonProvider<Collection<? extends HospitalityEntity>> {}

@Entity
@Table(name = "hotels", schema = "", catalog = "mydb")
public class HotelsEntity implements HospitalityEntity{
private int idHotel;
private String name;
private String region;
private String description;

@Id
@Column(name = "id_hotel")
public int getIdHotel() {
    return idHotel;
}

public void setIdHotel(int idHotel) {
    this.idHotel = idHotel;
}

@Basic
@Column(name = "name")
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Basic
@Column(name = "region")
public String getRegion() {
    return region;
}

public void setRegion(String region) {
    this.region = region;
}

@Basic
@Column(name = "description")
public String getDescription() {
    return description;
}

public void setDescription(String description) {
    this.description = description;
}


@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    HotelsEntity that = (HotelsEntity) o;

    if (idHotel != that.idHotel) return false;
    if (description != null ? !description.equals(that.description) : that.description != null) return false;
    if (name != null ? !name.equals(that.name) : that.name != null) return false;
    if (region != null ? !region.equals(that.region) : that.region != null) return false;

    return true;
}

@Override
public int hashCode() {
    int result = idHotel;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + (region != null ? region.hashCode() : 0);
    result = 31 * result + (description != null ? description.hashCode() : 0);
    return result;
}
}
  • In case you correctly included `jersey-media-moxy` or other things, you hit this error when you have no `@XmlJavaTypeAdapter` for custom classes. – koppor Feb 21 '17 at 17:04
  • 9
    If you've done all the things in the other answers, don't forget `clientConfig.register(JacksonJsonProvider.class);` – Erhannis Jun 05 '17 at 22:48
  • I think your problem could be for this @Override public boolean isReadable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return MediaType.APPLICATION_JSON.equals(mediaType.getType()) && MediaType.APPLICATION_JSON.equals(mediaType.getSubtype()); } The parameter mediaType is an instance and have type ("application") and subtype ("json") divided. Replace by return MediaType.valueOf(MediaType.APPLICATION_JSON).equals(mediaType); Hope this help – Luis Carlos Sep 18 '17 at 13:51
  • helped me when packaging uber jar – Marc T Feb 20 '19 at 13:05

8 Answers8

45

You can use jersey json library:

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

Or genson:

<dependency>
    <groupId>com.owlike</groupId>
    <artifactId>genson</artifactId>
    <version>1.3</version>
</dependency>
Denis535
  • 3,407
  • 4
  • 25
  • 36
  • 18
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – JAL Sep 27 '15 at 21:40
  • These libraries/dependencies are other options to process JSON data and most of them are compatible because they implement a standard Java Specification called JSR 353 or 374 - Java API for JSON Processing (JSON-P). All libraries that follow this specification are able to parse, generate, transform and query JSON in a standard way and are compatible with the Java Ecosystem. For more information about this specification see https://jcp.org/en/jsr/detail?id=353 and https://jcp.org/en/jsr/detail?id=374. Info about the Genson library implementation http://genson.io/ – brunocrt Sep 11 '19 at 18:26
24

Sorry to resurrect this post, but I was having this problem with a Maven project and found that I needed to include a dependency for jackson-jaxrs-json-provider in my pom:

<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.4.1</version>
</dependency>

MVN Repository: http://mvnrepository.com/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider

brokethebuildagain
  • 2,162
  • 1
  • 22
  • 44
17

Your entity class does not have an empty constructor, which is needed for the JAX-RS unmarshalling.

Have a look here:

https://blogs.oracle.com/groundside/entry/jax_rs_2_0_messagebodyreader

brasofilo
  • 25,496
  • 15
  • 91
  • 179
blacharnia
  • 2,124
  • 2
  • 15
  • 16
7

Check if you are registering the media type for JSON support. Do you have jersey-media-moxy on your class-path? If not, add this dependency to your pom.xml, please check your jersey version, in this example I'm using Jersey 2 (2.24)

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-moxy</artifactId>
        <version>2.24</version>
    </dependency>
brunocrt
  • 720
  • 9
  • 11
  • I also had to add [jaxb-runtime](https://stackoverflow.com/a/52872160/432903) and also [jersey-guava](https://stackoverflow.com/a/22069399/432903) – prayagupa Apr 07 '19 at 18:27
  • The jersey-media-moxy is Jersey JSON entity providers support module based on EclipseLink MOXy. MOXy implements JAXB allowing developers to provide their mapping information through annotations as well as providing support for storing the mappings in XML format and also enables to bind Java classes to XML or JSON More information on https://www.eclipse.org/eclipselink/#moxy – brunocrt Sep 11 '19 at 18:24
2

For Gradle, add the following dependency:

compile group: 'org.glassfish.jersey.media',
name         : 'jersey-media-moxy',
version      : '2.24.1'
gfullam
  • 11,531
  • 5
  • 50
  • 64
Purushothaman
  • 417
  • 4
  • 6
0

This very same exception is also thrown by jersey-media-moxy for silly mistakes like using an @XmlAttribute annotation where you really need an @XmlElement. This was the case in my scenario, and this is something to look into especially if the problem only happens for certain classes but others work fine.

raner
  • 1,175
  • 1
  • 11
  • 21
0

Adding this dependency has fixed my error while other suggested answers did not:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-jaxb</artifactId>
    <version>${jersey2.version}</version>
</dependency>
Hari Kiran
  • 188
  • 13
0

Could it be that you have dependencies to:

jersey-common-2.25.1.jar
jersey-media-json-jackson-2.25.1.jar

...?

I had that in a rich java application that performs client rest calls. When I assembled the executable jar for this client all dependencies gets unpacked into one jar file. It turned out that both these files got a file org.glassfish.jersey.internal.spi.AutoDiscoverable ...WITH DIFFERENT CONTENT!

Everything woked fine in my IDE, bit once assembled in one jar the content that was needed was overwiritten by the other one.

The solution turned out to be how the order, and with a couple of exclusions, in the dependencies was declared. I put this early in the list:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.25.1</version>
        <exclusions>
            <exclusion>
                <groupId>org.glassfish.jersey.core</groupId>
                <artifactId>jersey-common</artifactId>
            </exclusion>
            <exclusion>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
            </exclusion>
        </exclusions>
</dependency>

Later down I put:

 <dependency>   
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-common</artifactId>
      <version>2.25.1</version>
 </dependency>

I also excluded any other dependencies to jersey-common from other dependencies. Best regards Fredrik

Dharman
  • 30,962
  • 25
  • 85
  • 135
user2902165
  • 303
  • 2
  • 14