Java Jersey & Jackson ObjectMapper Example
When building REST APIs with Jersey (a JAX-RS implementation) and Jackson for JSON serialization, developers often need to customize how objects are converted to and from JSON. This is achieved by configuring a Custom ObjectMapper. ObjectMapper provides flexibility to control JSON formatting, null handling, date serialization, and more. Let us delve into understanding how to combine Java Jersey & Jackson ObjectMapper to help in seamless JSON serialization and deserialization in RESTful services.
1. Introduction
1.1 ObjectMapper
ObjectMapper is the core class in the Jackson library responsible for reading and writing JSON data. It provides APIs to serialize Java objects to JSON and deserialize JSON to Java objects. You can configure it with features such as:
- Property naming strategies (e.g.,
snake_casevscamelCase) - Inclusion/exclusion of
nullfields - Date format and timezone handling
- Pretty-printing JSON output for readability
- Custom serializers and deserializers for complex objects
1.2 JacksonJaxbJsonProvider
JacksonJaxbJsonProvider is a Jersey integration class that connects JAX-RS with Jackson. It allows Jersey to use Jackson for JSON (de)serialization in REST endpoints. By passing a custom ObjectMapper, you gain global control over how JSON is processed in your API.
1.3 ContextResolver
ContextResolver<ObjectMapper> is a JAX-RS extension point that allows you to supply custom context objects to Jersey at runtime. It is commonly used to provide a customized ObjectMapper for all REST resources, ensuring consistent JSON serialization/deserialization behavior.
2. Code Example
2.1 Add Dependencies
Before writing the application code, we need to include the required dependencies for Jersey, Grizzly HTTP server, and Jackson JSON provider in our pom.xml.
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>3.1.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>3.1.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.18.1</version>
</dependency>
</dependencies>
This configuration brings in the core Jersey libraries for RESTful services, the Grizzly HTTP server for lightweight deployment, and Jackson as the JSON serialization/deserialization engine.
2.2 Create the Model Class
The model class defines the data structure that will be serialized into JSON using Jackson. Here, we define a simple User class with name, address, and email fields.
package com.example.model;
public class User {
private String name;
private String address;
private String email;
public User() {}
public User(String name, String address, String email) {
this.name = name;
this.address = address;
this.email = email;
}
public String getName() { return name; }
public String getAddress() { return address; }
public String getEmail() { return email; }
public void setName(String name) { this.name = name; }
public void setAddress(String address) { this.address = address; }
public void setEmail(String email) { this.email = email; }
}
This class will serve as the response model for the REST endpoint, and Jackson will automatically convert it into JSON when returned by the API.
2.3 Create the Resource Class
The resource class exposes a REST endpoint using JAX-RS annotations. The @Path defines the route, and @Produces indicates that the response type is JSON.
package com.example.resource;
import com.example.model.User;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/user")
public class UserResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public User getUser() {
return new User("John Doe", null, "john@example.com");
}
}
This endpoint returns a User object when accessed, which will be serialized by Jackson into JSON output.
2.4 Create a Custom ObjectMapper
To control the way JSON is serialized (such as naming strategy, null inclusion, or formatting), we define a custom ObjectMapper configuration class.
package com.example.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationFeature;
public class CustomObjectMapper {
public static ObjectMapper create() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // Exclude null fields
mapper.enable(SerializationFeature.INDENT_OUTPUT); // Pretty print
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); // snake_case
return mapper;
}
}
This configuration ensures that JSON output is indented, excludes null fields, and uses snake_case for property names instead of camelCase.
2.5 Create a Context Resolver
The ContextResolver class provides the custom ObjectMapper to Jersey at runtime, allowing global control over JSON serialization for all REST endpoints.
package com.example.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.ext.ContextResolver;
import jakarta.ws.rs.ext.Provider;
@Provider
public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper = CustomObjectMapper.create();
@Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
This resolver acts as a bridge between Jersey and Jackson, ensuring that every REST response uses the same custom serialization settings defined in the CustomObjectMapper.
2.6 Configure the Jersey Application
The Jersey application class bootstraps the REST API by registering all providers and resources. It integrates the custom ObjectMapper and Jackson provider.
package com.example;
import com.example.config.CustomObjectMapper;
import com.example.config.ObjectMapperResolver;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.glassfish.jersey.server.ResourceConfig;
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
// Register REST resources automatically
packages("com.example.resource");
// Register ObjectMapper resolver
register(ObjectMapperResolver.class);
// Register Jackson JSON provider with custom ObjectMapper
register(new JacksonJaxbJsonProvider(CustomObjectMapper.create(),
JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS));
}
}
This setup ensures Jersey automatically detects all REST resources and uses the customized Jackson provider for consistent JSON handling across the application.
2.7 Create the Main Application
The main class launches an embedded Grizzly HTTP server that hosts the Jersey application. It starts the REST API and keeps it running to handle requests.
package com.example;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import java.net.URI;
public class App {
public static void main(String[] args) {
final String BASE_URI = "http://localhost:8080/api/";
final ResourceConfig rc = new JerseyApplication();
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
System.out.println("Server started at " + BASE_URI);
System.out.println("Visit http://localhost:8080/api/user to see JSON output");
try {
Thread.currentThread().join(); // Keep server running
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Running this class starts a lightweight Grizzly server, and visiting http://localhost:8080/api/user will display a JSON response with the user’s details, confirming that the custom ObjectMapper and Jersey setup work correctly.
2.8 Run the Code
Once all the classes are implemented, build and run the application from your IDE or using the command line. Ensure that your Maven project structure is correctly configured, and then execute the App class to start the embedded Grizzly HTTP server.
# Compile and run the application using Maven mvn clean compile exec:java -Dexec.mainClass="com.example.App"
After execution, the console should display server startup information, confirming that your REST service is live and accessible.
Server started at http://localhost:8080/api/ Visit http://localhost:8080/api/user to see JSON output
This indicates that the embedded Grizzly server is running, and you can now test the REST endpoint by opening the provided URL in your browser or using tools like curl or Postman.
2.9 Code Output
When you navigate to the endpoint http://localhost:8080/api/user, Jersey will invoke the UserResource class, serialize the User object using the customized ObjectMapper, and return the following JSON output.
{
"name": "John Doe",
"email": "john@example.com"
}
This JSON response demonstrates the effectiveness of the custom ObjectMapper configuration:
it excludes null fields (the address field), uses snake_case property naming, and formats the JSON output with indentation for readability.
The entire setup ensures consistent, clean, and maintainable JSON serialization across all your Jersey REST APIs.
3. Conclusion
By using a Custom ObjectMapper with JacksonJaxbJsonProvider and ContextResolver, you gain centralized control over your JSON serialization and deserialization logic in Jersey-based REST APIs. This approach enhances maintainability, ensures consistent API output, and allows for advanced configurations like custom serializers, naming strategies, and data inclusion rules across your application.

