Igor Kromin |   Consultant. Coder. Blogger. Tinkerer. Gamer.

Jersey makes it really easy to customise how your RESTful service accepts input and how it produces output. I've written previously about how, with a few lines of code it's possible to use Jackson to convert your POJOs to JSON when returning data from your service. This article looks at the other end of the lifecycle - the input, specifically how a JAX-RS service can process a POST message body and create a POJO as input to a Resource method parameter. In other words - how do you implement a custom message body reader?

First lets look at the Resource class...
@Produces({ "text/plain" })
public class HelloPostResource {
public String sayHello(Hello hello)
return "Hello " + hello.getName();

Quite straight forward. It's a resource that accepts POST requests on the /hello URI. The input is a Hello type object. The output is a String that ends up being something like "Hello {name}".

The Hello object is just as simple, it's a DTO that holds one value - the name...
public class Hello {
private String name;
public String getName() {
return name;
public void setName(String name) {
this.name = name;

Getting back to the HelloPostResource code, you might be thinking that there aren't any parameter annotations on the Hello object in the sayHello() method. That is correct. The hello object is instead populated by a MessageBodyReader. Lets see how that is done...
public class MyMessageBodyReader implements MessageBodyReader<Hello> {
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return (type == Hello.class);
public Hello readFrom(Class<Hello> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream inputStream)
throws IOException, WebApplicationException
try (BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream))) {
String name = br.readLine();
Hello hello = new Hello();
return hello;

There are two methods that the MessageBodyReader interface provides. The first - isReadable() - is used to determine whether that particular MessageBodyReader implementation is capable of handling the necessary data conversion. The second - readFrom() does the actual conversion from the POST message body to a Hello object.

When Jersey attempts to process the sayHello(Hello hello) method it invokes all registered MessageBodyReader implementations and the first that returns true from its isReadable() method will be called to process the POST body. In the case of MyMessageBodyReader, true is returned if the input type is equal to Hello.class. This is a very simple check but works in this case. It's possible to create more elaborate checks that take annotations and the media type into account by using the relevant parameters passed to isReadable().

The readFrom() method has all of the same parameters as isReadable() plus a map of HTTP headers and the input stream representing the POST body itself. In the case of MyMessageBodyReader, the only input parameter that is used is the input stream. The implementation in this case is very simplistic - the first line of the input stream is read and used as the 'name' attribute, any other lines are ignored. Then, the Hello object is instantiated, it's name is set and the object is returned. That is the object that gets passed along to the sayHello(Hello hello) method on the HelloPostResource class.

In a more complex implementation the input stream could be used together with a JSON or XML parser to read the body text and convert to Java objects for example.

Invoking the service using Postman renders these results...

It works as expected! A real implementation would of course be more involved, however the code shown in this article shows how to implement a custom message body reader that can be used as a template.

Don't forget to register() the MyMessageBodyReader class or add the @Provider annotation if you're using automatic feature discovery.


A quick disclaimer...

Although I put in a great effort into researching all the topics I cover, mistakes can happen. Use of any information from my blog posts should be at own risk and I do not hold any liability towards any information misuse or damages caused by following any of my posts.

All content and opinions expressed on this Blog are my own and do not represent the opinions of my employer (Oracle). Use of any information contained in this blog post/article is subject to this disclaimer.
Hi! You can search my blog here ⤵
NOTE: (2022) This Blog is no longer maintained and I will not be answering any emails or comments.

I am now focusing on Atari Gamer.