Quarkus deployment extension that provides build-time configuration and processing for integrating Jackson JSON serialization with the reactive REST client.
npx @tessl/cli install tessl/maven-io-quarkus--quarkus-rest-client-jackson-deployment@3.23.0A Quarkus deployment extension that provides build-time configuration and processing for integrating Jackson JSON serialization/deserialization with the reactive REST client. This extension handles the automatic registration of Jackson message body readers and writers for JSON processing in REST client implementations, enabling seamless JSON data binding for client-side REST communication in Quarkus applications.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-jackson-deployment</artifactId>
<version>3.23.0</version>
</dependency>Add via Quarkus CLI:
quarkus extension add rest-client-reactive-jacksonFor deployment extension usage (typically in build processors):
import io.quarkus.rest.client.reactive.jackson.deployment.RestClientReactiveJacksonProcessor;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.annotations.BuildStep;For runtime usage (in REST client interfaces):
import io.quarkus.rest.client.reactive.jackson.ClientObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.JacksonUtil;For message body processing:
import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.ClientJacksonMessageBodyReader;
import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.ClientJacksonMessageBodyWriter;
import org.jboss.resteasy.reactive.client.spi.RestClientRequestContext;The extension is automatically activated when added to your Quarkus project. No additional configuration is required for basic JSON processing.
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@RegisterRestClient(baseUri = "https://api.example.com")
public interface UserService {
@GET
@Path("/users/{id}")
@Produces(MediaType.APPLICATION_JSON)
User getUser(@PathParam("id") Long id);
@POST
@Path("/users")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
User createUser(CreateUserRequest request);
}Define a custom ObjectMapper for specific REST client configurations:
import io.quarkus.rest.client.reactive.jackson.ClientObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
@RegisterRestClient(baseUri = "https://api.example.com")
public interface UserService {
@ClientObjectMapper
static ObjectMapper objectMapper(ObjectMapper defaultObjectMapper) {
return defaultObjectMapper.copy()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
}
@GET
@Path("/users/{id}")
User getUser(@PathParam("id") Long id);
}The extension integrates with Quarkus' build-time optimization system and consists of several key components:
RestClientReactiveJacksonProcessor handles feature registration and bean configuration during buildAutomatically registers the REST_CLIENT_JACKSON feature and configures required components.
public class RestClientReactiveJacksonProcessor {
/**
* Registers the REST_CLIENT_JACKSON feature
*/
@BuildStep
void feature(BuildProducer<FeatureBuildItem> features);
/**
* Configures Vert.x JSON reinitialization for native compilation
*/
@BuildStep
ReinitializeVertxJsonBuildItem vertxJson();
/**
* Registers ClientObjectMapper annotation for client context resolution
*/
@BuildStep
void additionalProviders(BuildProducer<AnnotationToRegisterIntoClientContextBuildItem> annotation);
/**
* Registers Jackson message body readers and writers as CDI beans.
* Only registers if Jackson provider is already defined in the application.
*/
@BuildStep
void additionalProviders(
List<ResteasyReactiveJacksonProviderDefinedBuildItem> jacksonProviderDefined,
BuildProducer<AdditionalBeanBuildItem> additionalBean,
BuildProducer<MessageBodyReaderBuildItem> additionalReaders,
BuildProducer<MessageBodyWriterBuildItem> additionalWriters);
/**
* Configures native image support by registering cleanup task service provider
*/
@BuildStep
void nativeSupport(BuildProducer<ServiceProviderBuildItem> serviceProviderProducer);
}Annotation for defining per-client ObjectMapper instances with custom configuration.
/**
* Annotation for defining custom ObjectMapper for specific REST client.
* Must be placed on static methods in REST Client interfaces.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ClientObjectMapper {
}Usage Requirements:
copy() method on default mapper to inherit base settingsUsage Examples:
Simple custom mapper:
@ClientObjectMapper
static ObjectMapper objectMapper() {
return new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}Inheriting from default mapper:
@ClientObjectMapper
static ObjectMapper objectMapper(ObjectMapper defaultObjectMapper) {
return defaultObjectMapper.copy()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
}Utility for resolving ObjectMapper instances from JAX-RS context, enabling per-client customization.
/**
* Utility class for resolving ObjectMapper from REST client context
*/
final class JacksonUtil {
/**
* Resolves ObjectMapper from REST client context, falling back to default if not found.
* Supports per-client ObjectMapper customization via @ClientObjectMapper annotation.
*
* @param responseMediaType the media type of the response
* @param context the REST client request context containing configuration
* @return ObjectMapper instance (custom or default)
*/
static ObjectMapper getObjectMapperFromContext(MediaType responseMediaType, RestClientRequestContext context);
}Runtime components that handle JSON serialization and deserialization for REST client communication.
/**
* Jackson-based message body reader for client-side JSON deserialization
*/
public class ClientJacksonMessageBodyReader extends AbstractJsonMessageBodyReader
implements ClientMessageBodyReader<Object> {
private final ObjectMapper mapper;
/**
* Constructor with ObjectMapper dependency injection.
* The injected mapper serves as default but can be overridden per-client.
*
* @param mapper default ObjectMapper instance injected by CDI
*/
@Inject
public ClientJacksonMessageBodyReader(ObjectMapper mapper);
/**
* Standard JAX-RS reader method for JSON deserialization.
* Uses default ObjectMapper without context resolution.
*/
@Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException;
/**
* Client-specific reader method with REST client context support.
* Resolves ObjectMapper from context using JacksonUtil, enabling per-client customization.
*/
@Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
InputStream entityStream, RestClientRequestContext context)
throws IOException, WebApplicationException;
}
/**
* Jackson-based message body writer for client-side JSON serialization
*/
public class ClientJacksonMessageBodyWriter implements ClientMessageBodyWriter<Object> {
private final ObjectMapper mapper;
/**
* Constructor with ObjectMapper dependency injection.
* The injected mapper serves as default but can be overridden per-client.
*
* @param mapper default ObjectMapper instance injected by CDI
*/
@Inject
public ClientJacksonMessageBodyWriter(ObjectMapper mapper);
/**
* Indicates if type is writable for JSON serialization.
* Always returns true for JSON media types.
*
* @return true if the type can be serialized to JSON
*/
@Override
public boolean isWriteable(Class type, Type genericType, Annotation[] annotations,
MediaType mediaType);
/**
* Standard JAX-RS writer method for JSON serialization.
* Uses default ObjectMapper without context resolution.
*/
@Override
public void writeTo(Object o, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException;
/**
* Client-specific writer method with REST client context support.
* Resolves ObjectMapper from context using JacksonUtil, enabling per-client customization.
*/
@Override
public void writeTo(Object o, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream, RestClientRequestContext context)
throws IOException, WebApplicationException;
}Automatic cleanup of ObjectMapper caches when REST clients are closed.
/**
* Cleanup task for ClientObjectMapper mappings when REST client is closed
*/
public class JacksonCleanupRestClientClosingTask implements RestClientClosingTask {
/**
* Cleans up cached ObjectMapper mappings for the closing client
*/
@Override
public void close(Context context);
}application/json - Standard JSON formatapplication/x-ndjson - Newline delimited JSONapplication/stream+json - JSON streaming formatapplication/json - Standard JSON formatThe extension handles JSON processing errors gracefully:
ClientWebApplicationException with HTTP 200 statusThe extension uses the standard Quarkus configuration prefix quarkus.rest-client-reactive. for any REST client related settings.
io.quarkus:quarkus-rest-jackson-common-deployment - Common Jackson deployment utilitiesio.quarkus:quarkus-rest-client-deployment - REST client deployment foundationio.quarkus:quarkus-rest-client-jackson - Runtime componentscom.fasterxml.jackson.databind.ObjectMapper for JSON processingjakarta.ws.rs APIs for HTTP handlingorg.jboss.resteasy.reactive framework integrationio.vertx.core.json for JsonObject and JsonArray supportjakarta.inject for dependency injection/**
* Key class for caching ObjectMapper resolvers per REST client configuration
*/
public final class ResolverMapKey {
/**
* Creates a new resolver map key
*/
public ResolverMapKey(Configuration configuration, Class<?> restClientClass);
/**
* Gets the JAX-RS configuration
*/
public Configuration getConfiguration();
/**
* Gets the REST client class
*/
public Class<?> getRestClientClass();
@Override
public boolean equals(Object o);
@Override
public int hashCode();
}
/**
* Build item indicating that Vert.x JSON needs reinitialization for native compilation
*/
public final class ReinitializeVertxJsonBuildItem extends SimpleBuildItem {
}
/**
* Build item for registering annotations that should be available in client context
*/
public final class AnnotationToRegisterIntoClientContextBuildItem extends MultiBuildItem {
public AnnotationToRegisterIntoClientContextBuildItem(DotName annotation);
public DotName getAnnotation();
}
/**
* Marker build item indicating Jackson provider has been defined for RESTEasy Reactive
*/
public final class ResteasyReactiveJacksonProviderDefinedBuildItem extends SimpleBuildItem {
}