JAX-RS List Generic Type Erasure
When using JAX-RS resource methods, be sure you know how to maintain your Lists' generic types. This guide will tell you how to handle type erasure.
Join the DZone community and get the full member experience.
Join For FreeWant to maintain a List's generic type? The problem is that when you want to return a List from a JAX-RS resource method, the generic type is lost.
The following code results in type loss:
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllBooks() {
List<Book> books = BookRepository.getAllBooks(); // queries database for all books
return Response.ok(books).build();
}
And the following exception:
MessageBodyWriter not found for media type=application/json,
type=class java.util.Arrays$ArrayList,
genericType=class java.util.Arrays$ArrayList
Luckily, JAX-RS is packaged with a solution in the form of the GenericEntity class, which is designed to maintain the generic type. To use this class, just wrap the Collection in the GenericEntity like shown in this code:
import javax.ws.rs.core.GenericEntity;
...
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllBooks() {
List<Book> books = BookRepository.getAllBooks(); // queries database for all books
GenericEntity<List<Book>> list = new GenericEntity<List<Book>>(books) {};
return Response.ok(list).build();
}
I was banging my head against the wall trying to figure this out, but thanks to this post by Adam Bein I was saved. Hopefully, this post finds you and stops any headaches before they begin.
@XmlRootElement
public class Book {
private String isbn;
private String title;
private String author;
private Float price;
public Book() {
}
public Book(String isbn, String title, String author, Float price){
this.isbn = isbn;
this.title = title;
this.author = author;
this.price = price;
}
// Getters and Setters removed for brevity
}
Important Update
The behavior described above is demonstrated on GlassFish 4.1 and produces the following exception in the server.log files:
[2017-08-30T20:29:56.489+0100] [glassfish 4.1] [SEVERE] []
[org.glassfish.jersey.message.internal.WriterInterceptorExecutor]
[tid: _ThreadID=70 _ThreadName=http-listener-1(2)] [timeMillis: 1504121396489] [levelValue: 1000]
[[MessageBodyWriter not found for media type=application/json,
type=class java.util.ArrayList,
genericType=class java.util.ArrayList.]]
However, the same behavior is not seen on IBM WebSphere Liberty Profile. In fact, no error is thrown and the List of books is successfully serialized to a JSON representation. Further investigation shows that Liberty Profile is far more forgiving of deviations from the specification. More detailed research is needed to fully document the difference between server implementations I have only looked at GlassFish and Liberty Profile. However, as GlassFish is the reference implementation for Java EE, my advice is to develop with reference to its expectations because all other servers implementations should at least conform to its requirements.
The source code for this article is in the readlearncode_articles GitHub repository.
Further Reading
I regularly blog about Java EE on my blog readlearncode.com where I have recently published a mini-series of articles on the JAX-RS API.
Among the articles, there are discussions on bean validation failure in REST endpoints, how to work with Consumers and Producers, and how to create JAX-RS Resource Entities.
Do you want to know all the ways the @Context (javax.ws.rs.core.context) annotation can be used within your JAX-RS application. If so take a look at this five-parts series:
- What is javax.ws.rs.core.context? (Part 1)
- What is javax.ws.rs.core.context? (Part 2)
- What is javax.ws.rs.core.context? (Part 3)
- What is javax.ws.rs.core.context? (Part 4)
- What is javax.ws.rs.core.context? (Part 5)
Published at DZone with permission of Alex Theedom, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments