Jersey web service could use MessageBodyReader to deserialize JSON request body to Java object and use MessageBodyWriter to serialize Java object to JSON in response body
Create the following folder structure:

pom.xml: (if using Gradle, replace pom.xml with build.gradle)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.maxjava</groupId> <artifactId>JerseyMessageBodyReaderWriter</artifactId> <version>1.0</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet-core</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>org.glassfish.jersey.inject</groupId> <artifactId>jersey-hk2</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> </plugins> </build> </project>
plugins { id 'java' id 'war' } sourceCompatibility = 11 targetCompatibility = 11 group 'net.maxjava' version '1.0' repositories { mavenCentral() } dependencies { implementation group: 'org.glassfish.jersey.containers', name: 'jersey-container-servlet-core', version: '2.29.1' implementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.29.1' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9' }
Some notes about pom.xml:
- jersey-container-servlet-core and jersey-hk2 is needed for using Jersey as servlet
- jackson-databind is for JSON serialization and deserialization
- maven-compiler-plugin is for compiling the code using source version 11 and target version 11
- A war artifact will be created for deployment to servlet containers
Some notes about gradle.build:
- jersey-container-servlet-core and jersey-hk2 is needed for using Jersey as servlet
- jackson-databind is for JSON serialization and deserialization
- targetCompatibility is used to configure compilation with source version 11 and target version 11
- A war artifact will be created for deployment to servlet containers
web.xml:
<web-app> <display-name>Jersey Web Service with MessageBodyReader and Writer</display-name> <servlet> <servlet-name>JerseyWebServiceMessageBodyReaderWriter</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>application.MyApplication</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JerseyWebServiceMessageBodyReaderWriter</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
MyApplication.java:
package application; import messagebody.MyMessageBodyReader; import messagebody.MyMessageBodyWriter; import org.glassfish.jersey.server.ResourceConfig; import resource.MyResource; public class MyApplication extends ResourceConfig { public MyApplication() { register(MyResource.class); register(MyMessageBodyReader.class); register(MyMessageBodyWriter.class); } }
Some notes about MyApplication.java:
- This is main configuration class and has to be specified in web.xml
- In the constructor, register the resource classes, which are annotated with JAX-RS for web services, and register the MyMessageBodyReader and MyMessageBodyWriter class
MyMessageBodyReader.java:
package messagebody; import com.fasterxml.jackson.databind.ObjectMapper; import org.glassfish.jersey.server.ResourceConfig; import resource.MyResource; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.Arrays; public class MyMessageBodyReader<T> implements MessageBodyReader<T> { @Override public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { System.out.println("isReadable " + mediaType.getType()); if (MediaType.APPLICATION_JSON.equals(mediaType.getType() + "/" + mediaType.getSubtype())) { return true; } else { return false; } } @Override public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { System.out.println("readFrom " + mediaType.getType()); ObjectMapper objectMapper = new ObjectMapper(); InputStreamReader osw = new InputStreamReader(entityStream, StandardCharsets.UTF_8); int result = -1; int offset = 0; int BUFFER_SIZE = 1024; char cbuf[] = new char[BUFFER_SIZE]; String strInput = ""; do { result = osw.read(cbuf, offset, BUFFER_SIZE); strInput += new String(Arrays.copyOfRange(cbuf, 0, result)); } while (result == BUFFER_SIZE); osw.close(); return objectMapper.readValue(strInput, type); } }
Some notes about MyMessageBodyReader.java:
- Override isReadable to return true for only media type "application/json", since we only want MyMessageBodyReader to deserialize JSON
- Override readFrom for the actual deserialization implementation
MyMessageBodyWriter.java:
package messagebody; import com.fasterxml.jackson.databind.ObjectMapper; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; public class MyMessageBodyWriter<T> implements MessageBodyWriter<T> { @Override public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { System.out.println("isWriteable " + mediaType.getType()); if (MediaType.APPLICATION_JSON.equals(mediaType.getType() + "/" + mediaType.getSubtype())) { return true; } else { return false; } } @Override public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { System.out.println("writeTo " + mediaType.getType()); ObjectMapper objectMapper = new ObjectMapper(); OutputStreamWriter osw = new OutputStreamWriter(entityStream, StandardCharsets.UTF_8); osw.write(objectMapper.writeValueAsString(t)); osw.close(); } }
Some notes about MyMessageBodyWriter.java:
- Override isWriteable to return true for only media type "application/json", since we only want MyMessageBodyWriter to serialize JSON
- Override writeTo for the actual serialization implementation
MyResource.java:
package resource; import model.MyRequest; import model.MyResponse; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.Calendar; @Path("/myapp") public class MyResource { @POST @Path("/square") @Produces(MediaType.APPLICATION_JSON) public Response getSquare(MyRequest myRequest) { MyResponse myResponse = new MyResponse(); String response = "Hi " + myRequest.getName() + " ! " + myRequest.getNumber() + "'s square = " + myRequest.getNumber() * myRequest.getNumber(); myResponse.setResponse(response); myResponse.setCurrentTime(Calendar.getInstance().getTime().toString()); return Response.ok(myResponse).status(Response.Status.OK).build(); } }
Some notes about MyResource.java:
- @Produces(MediaType.APPLICATION_JSON) to set the response header content type to "application/json", this is needed for MyMessageBodyWriter
- MyRequest is the request body(will be deserialized by MyMessageBodyReader from JSON) where MyResponse is the response body(will be serialized by MyMessageBodyWriter to JSON)
MyRequest.java:
package model; public class MyRequest { private String name; private Integer number; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } }
MyResponse.java:
package model; public class MyResponse { private String response; private String currentTime; public String getResponse() { return response; } public void setResponse(String response) { this.response = response; } public String getCurrentTime() { return currentTime; } public void setCurrentTime(String currentTime) { this.currentTime = currentTime; } }
Sample output:
curl --request POST '<CONTEXT_PATH>/myapp/square' --header 'Content-Type: application/json' --data-raw ' { "name": "Sam", "number": 210 }'
Response:
{ "response": "Hi Sam ! 210's square = 44100", "currentTime": "Sat Oct 08 17:21:11 PDT 2022" }