1. Introduction

The RESTEasy Jackson Provider integrates Jackson 3 with Jakarta REST applications running on RESTEasy. It provides JSON serialization and deserialization using Jackson’s high-performance data-binding capabilities.

This provider is a standalone project with its own release cycle, separate from the main RESTEasy project. It uses Jackson 3 (tools.jackson coordinates), not Jackson 2 (com.fasterxml.jackson). For the older Jackson 2 integration, see the org.jboss.resteasy:resteasy-jackson2-provider artifact documented in the main RESTEasy documentation.

Features include:

  • JSON serialization and deserialization with Jackson 3

  • Support for application/json, application/*+json, and text/json media types

  • JSON Patch (RFC 6902) support

  • JSON Merge Patch (RFC 7396) support

  • Polymorphic type handling with allow-list validation

  • Custom JsonMapper configuration via ContextResolver

  • RESTEasy tracing integration

1.1. Version Information

This guide is for RESTEasy Jackson Provider version 1.0.0.Beta1.

2. Getting Started

2.1. Maven Dependencies

Add the RESTEasy Jackson Provider to the project:

<dependency>
    <groupId>dev.resteasy.providers</groupId>
    <artifactId>resteasy-jackson-provider</artifactId>
    <version>1.0.0.Beta1</version>
</dependency>

The provider auto-registers via the jakarta.ws.rs.core.Feature service loader mechanism. No additional configuration is needed — once the dependency is on the classpath, JSON support is available.

2.2. Basic JSON Endpoints

The provider automatically handles serialization and deserialization of Java objects to and from JSON.

Reading JSON:

@Path("/customers")
public class CustomerResource {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response createCustomer(Customer customer) {
        // customer is deserialized from the JSON request body
        save(customer);
        return Response.status(Response.Status.CREATED).entity(customer).build();
    }

    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Customer getCustomer(@PathParam("id") String id) {
        // the returned object is serialized to JSON
        return findCustomer(id);
    }
}

2.3. Supported Media Types

The provider handles the following media types:

  • application/json

  • application/*+json (vendor-specific JSON media types)

  • text/json

The application/*+json wildcard allows the use of custom JSON-based media types:

@GET
@Produces("application/vnd.customer+json")
public Customer getCustomer() {
    return findCustomer();
}

2.4. Migration from resteasy-jackson2-provider

This provider uses Jackson 3, which has different Maven coordinates and package names from Jackson 2.

Jackson 2 (resteasy-jackson2-provider) Jackson 3 (resteasy-jackson-provider)

com.fasterxml.jackson packages

tools.jackson packages

com.fasterxml.jackson.databind.ObjectMapper

tools.jackson.databind.json.JsonMapper

Group ID: org.jboss.resteasy

Group ID: dev.resteasy.providers

com.fasterxml.jackson.core.JsonProcessingException

tools.jackson.core.JacksonException

resteasy.jackson.deserialization.whitelist.* properties

dev.resteasy.jackson.deserialization.allowlist.* properties

3. JSON Patch and JSON Merge Patch

The provider includes support for modifying resources using JSON Patch (RFC 6902) and JSON Merge Patch (RFC 7396). Both use the dev.resteasy.providers.jackson.ObjectPatch interface as the parameter type in resource methods. The media type on @Consumes determines which patch format is used.

3.1. JSON Patch (RFC 6902)

JSON Patch expresses a sequence of operations (add, remove, replace, move, copy, test) to apply to a JSON document. Use the media type application/json-patch+json.

@PATCH
@Path("/customer/{id}")
@Consumes("application/json-patch+json")
@Produces(MediaType.APPLICATION_JSON)
public Customer patchCustomer(@PathParam("id") String id, ObjectPatch patch) {
    Customer customer = findCustomer(id);
    return patch.apply(customer);
}

Example request body:

[
    {"op": "replace", "path": "/name", "value": "New Name"},
    {"op": "add", "path": "/tags/-", "value": "new-tag"},
    {"op": "remove", "path": "/temporary"}
]

The supported operations are:

Operation Description

add

Add a value to an object or array

remove

Remove a value from an object or array

replace

Replace an existing value

move

Move a value from one path to another

copy

Copy a value from one path to another

test

Test that a value at a path equals the specified value

Patch application is atomic — a deep copy of the target object is made before applying operations. If any operation fails, the original object is unchanged.

3.2. JSON Merge Patch (RFC 7396)

JSON Merge Patch provides a simpler patching mechanism. Use the media type application/merge-patch+json.

@PATCH
@Path("/customer/{id}")
@Consumes("application/merge-patch+json")
@Produces(MediaType.APPLICATION_JSON)
public Customer mergePatchCustomer(@PathParam("id") String id, ObjectPatch patch) {
    Customer customer = findCustomer(id);
    return patch.apply(customer);
}

Example request body:

{
    "name": "Updated Name",
    "email": null
}

Merge patch semantics:

  • Setting a field to a value updates that field

  • Setting a field to null removes it

  • Object values are merged recursively

  • Array values are replaced entirely (not merged element-by-element)

4. Configuration

4.1. Custom JsonMapper

To customize Jackson’s behavior, register a ContextResolver<JsonMapper> as a Jakarta REST provider. The provider looks up the JsonMapper via this resolver before falling back to a default mapper.

@Provider
public class CustomJsonMapperProvider implements ContextResolver<JsonMapper> {

    @Override
    public JsonMapper getContext(final Class<?> type) {
        return JsonMapper.builder()
                .enable(SerializationFeature.INDENT_OUTPUT)
                .build();
    }
}

The ContextResolver receives the type being serialized or deserialized, allowing different mapper configurations per type if needed.

4.2. Polymorphic Type Validation

For security, the provider installs a polymorphic type validator by default. This controls which classes are allowed during polymorphic deserialization, preventing deserialization attacks.

The allow-list is configured via system properties or RESTEasy configuration properties:

Property Description

dev.resteasy.jackson.deserialization.allowlist.allowIfBaseType

Comma-separated prefixes for allowed base types. Use * to allow any base type.

dev.resteasy.jackson.deserialization.allowlist.allowIfSubType

Comma-separated prefixes for allowed subtypes. Use * to allow any subtype.

Example configuration allowing types in the com.example package:

dev.resteasy.jackson.deserialization.allowlist.allowIfSubType=com.example

4.3. Exception Handling

The provider includes a JsonProcessingExceptionMapper that maps Jackson’s JacksonException to an HTTP 400 (Bad Request) response. The response body contains a generic error message. Exception details are logged at ERROR level but are not exposed in the response to avoid leaking internal information.

4.4. ObjectWriter and ObjectReader Modifiers

The provider supports customizing Jackson’s ObjectWriter and ObjectReader at request time via modifier injection. This allows you to alter serialization or deserialization behavior on a per-request basis: for example, enabling indented output, adding response headers, or enforcing strict deserialization rules.

To inject a modifier, register a ContextResolver<ObjectWriterModifier> or ContextResolver<ObjectReaderModifier> as a Jakarta REST provider. The provider looks up the modifier via this resolver before each write or read operation.

4.4.1. ObjectWriterModifier

An ObjectWriterModifier can modify the ObjectWriter and the response headers before serialization.

public class IndentingWriterModifier extends ObjectWriterModifier {

    @Override
    public ObjectWriter modify(final EndpointConfigBase<?> endpoint,
            final MultivaluedMap<String, Object> responseHeaders,
            final Object valueToWrite,
            final ObjectWriter w) throws JacksonException {
        return w.with(SerializationFeature.INDENT_OUTPUT);
    }
}

Register the modifier via a ContextResolver:

@Provider
public class WriterModifierResolver implements ContextResolver<ObjectWriterModifier> {

    @Override
    public ObjectWriterModifier getContext(final Class<?> type) {
        return new IndentingWriterModifier();
    }
}

4.4.2. ObjectReaderModifier

An ObjectReaderModifier can modify the ObjectReader and the JsonParser before deserialization.

public class StrictReaderModifier extends ObjectReaderModifier {

    @Override
    public ObjectReader modify(final EndpointConfigBase<?> endpoint,
            final MultivaluedMap<String, String> httpHeaders,
            final JavaType resultType,
            final ObjectReader r,
            final JsonParser p) throws JacksonException {
        return r.with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    }
}

Register the modifier via a ContextResolver:

@Provider
public class ReaderModifierResolver implements ContextResolver<ObjectReaderModifier> {

    @Override
    public ObjectReaderModifier getContext(final Class<?> type) {
        return new StrictReaderModifier();
    }
}

The ContextResolver receives the type being serialized or deserialized, allowing different modifier behavior per type if needed.

Jackson also provides ObjectWriterInjector and ObjectReaderInjector, which use a ThreadLocal to pass a modifier from a filter into the provider. These work when the filter and the resource endpoint execute on the same thread, but are unreliable in Jakarta EE containers where the request may be dispatched across threads. The ContextResolver approach is recommended as it is container-safe and does not depend on thread affinity. If both a ThreadLocal modifier and a ContextResolver are present, the ThreadLocal modifier takes precedence and the ContextResolver is not consulted.

4.5. Tracing Integration

The provider includes a JacksonJsonFormatRESTEasyTracingInfo implementation that provides JSON-formatted tracing output when RESTEasy’s tracing feature is enabled. This is auto-registered via the META-INF/services/org.jboss.resteasy.tracing.api.RESTEasyTracingInfo service loader mechanism and requires no additional configuration.