Core Java

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_case vs camelCase)
  • Inclusion/exclusion of null fields
  • 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.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button