docs
reference
tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.
Quarkus REST provides deep Jackson integration for JSON serialization and deserialization, including method-level customization, security-aware field filtering, and custom ObjectMapper configuration.
import io.quarkus.resteasy.reactive.jackson.CustomSerialization;
import io.quarkus.resteasy.reactive.jackson.CustomDeserialization;
import io.quarkus.resteasy.reactive.jackson.SecureField;
import io.quarkus.resteasy.reactive.jackson.EnableSecureSerialization;
import io.quarkus.resteasy.reactive.jackson.DisableSecureSerialization;
import io.quarkus.rest.client.reactive.jackson.ClientObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.ObjectReader;
import java.util.function.BiFunction;
import java.lang.reflect.Type;Provides method-level custom Jackson serialization by specifying a function that creates an ObjectWriter.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomSerialization {
/**
* BiFunction that takes ObjectMapper and Type, returns ObjectWriter
*/
Class<? extends BiFunction<ObjectMapper, Type, ObjectWriter>> value();
}Provides method-level custom Jackson deserialization by specifying a function that creates an ObjectReader.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomDeserialization {
/**
* BiFunction that takes ObjectMapper and Type, returns ObjectReader
*/
Class<? extends BiFunction<ObjectMapper, Type, ObjectReader>> value();
}Marks fields for role-based serialization filtering.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecureField {
/**
* Roles allowed to see this field
*/
String[] rolesAllowed();
}Enables secure serialization for a resource method or class.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableSecureSerialization {
}Disables secure serialization for a resource method or class.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisableSecureSerialization {
}Registers a custom ObjectMapper provider for REST Client.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientObjectMapper {
}public class CustomUserWriter implements BiFunction<ObjectMapper, Type, ObjectWriter> {
@Override
public ObjectWriter apply(ObjectMapper mapper, Type type) {
return mapper.writerWithView(PublicView.class);
}
}
@GET
@Path("/users/{id}")
@CustomSerialization(CustomUserWriter.class)
public User getUser(@RestPath String id) {
return userService.findById(id);
}public class Views {
public static class Public {}
public static class Internal extends Public {}
public static class Admin extends Internal {}
}
public class User {
@JsonView(Views.Public.class)
private String name;
@JsonView(Views.Internal.class)
private String email;
@JsonView(Views.Admin.class)
private String ssn;
}
public class PublicViewWriter implements BiFunction<ObjectMapper, Type, ObjectWriter> {
@Override
public ObjectWriter apply(ObjectMapper mapper, Type type) {
return mapper.writerWithView(Views.Public.class);
}
}
public class AdminViewWriter implements BiFunction<ObjectMapper, Type, ObjectWriter> {
@Override
public ObjectWriter apply(ObjectMapper mapper, Type type) {
return mapper.writerWithView(Views.Admin.class);
}
}
@Path("/users")
public class UserResource {
@GET
@Path("/{id}")
@CustomSerialization(PublicViewWriter.class)
public User getPublicUser(@RestPath String id) {
return userService.findById(id);
// Returns: { "name": "John" }
}
@GET
@Path("/{id}/admin")
@CustomSerialization(AdminViewWriter.class)
public User getAdminUser(@RestPath String id) {
return userService.findById(id);
// Returns: { "name": "John", "email": "john@example.com", "ssn": "123-45-6789" }
}
}public class SensitiveDataWriter implements BiFunction<ObjectMapper, Type, ObjectWriter> {
@Override
public ObjectWriter apply(ObjectMapper mapper, Type type) {
SimpleFilterProvider filters = new SimpleFilterProvider();
filters.addFilter("sensitiveFilter",
SimpleBeanPropertyFilter.serializeAllExcept("password", "token"));
return mapper.writer(filters);
}
}
@JsonFilter("sensitiveFilter")
public class Account {
private String username;
private String password; // Filtered out
private String token; // Filtered out
private String email;
}
@GET
@Path("/account")
@CustomSerialization(SensitiveDataWriter.class)
public Account getAccount() {
return accountService.getCurrentAccount();
}public class StrictUserReader implements BiFunction<ObjectMapper, Type, ObjectReader> {
@Override
public ObjectReader apply(ObjectMapper mapper, Type type) {
return mapper.reader()
.without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.forType(mapper.constructType(type));
}
}
@POST
@Path("/users")
@CustomDeserialization(StrictUserReader.class)
public User createUser(User user) {
return userService.create(user);
}public class CustomDateReader implements BiFunction<ObjectMapper, Type, ObjectReader> {
@Override
public ObjectReader apply(ObjectMapper mapper, Type type) {
ObjectMapper customMapper = mapper.copy();
customMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
return customMapper.readerFor(mapper.constructType(type));
}
}
@POST
@Path("/events")
@CustomDeserialization(CustomDateReader.class)
public Event createEvent(Event event) {
return eventService.create(event);
}public class PolymorphicReader implements BiFunction<ObjectMapper, Type, ObjectReader> {
@Override
public ObjectReader apply(ObjectMapper mapper, Type type) {
return mapper.readerFor(mapper.constructType(type))
.without(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public abstract class Animal {
private String name;
}
@POST
@Path("/animals")
@CustomDeserialization(PolymorphicReader.class)
public Animal createAnimal(Animal animal) {
return animalService.create(animal);
}@EnableSecureSerialization
public class User {
private String name;
@SecureField(rolesAllowed = {"ADMIN"})
private String email;
@SecureField(rolesAllowed = {"ADMIN", "MANAGER"})
private String phone;
@SecureField(rolesAllowed = {"ADMIN"})
private String ssn;
}
@GET
@Path("/users/{id}")
public User getUser(@RestPath String id) {
// Returns different fields based on user's roles
// Regular user: { "name": "John" }
// Manager: { "name": "John", "phone": "555-1234" }
// Admin: { "name": "John", "email": "john@example.com", "phone": "555-1234", "ssn": "123-45-6789" }
return userService.findById(id);
}@EnableSecureSerialization
@Path("/users")
public class UserResource {
@GET
@Path("/{id}")
public User getUser(@RestPath String id) {
// Secure serialization enabled for this method
return userService.findById(id);
}
@GET
@Path("/{id}/public")
@DisableSecureSerialization
public User getPublicUser(@RestPath String id) {
// Secure serialization disabled for this method
return userService.findById(id);
}
}public class Account {
private String id;
private String username;
@SecureField(rolesAllowed = {"ADMIN"})
private String password;
@SecureField(rolesAllowed = {"ADMIN", "AUDITOR"})
private List<Transaction> transactions;
@SecureField(rolesAllowed = {"ADMIN", "SUPPORT"})
private String internalNotes;
}@EnableSecureSerialization
public class Order {
private String orderId;
private User user; // User also has @SecureField annotations
private List<Item> items;
@SecureField(rolesAllowed = {"ADMIN", "FINANCE"})
private BigDecimal totalCost;
@SecureField(rolesAllowed = {"ADMIN"})
private String internalNotes;
}
@EnableSecureSerialization
public class User {
private String name;
@SecureField(rolesAllowed = {"ADMIN"})
private String email;
}@ClientObjectMapper
public static class CustomObjectMapperProvider implements ObjectMapperCustomizer {
@Override
public void customize(ObjectMapper objectMapper) {
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new JavaTimeModule());
}
}
@Path("/api")
@RegisterRestClient
@RegisterProvider(CustomObjectMapperProvider.class)
public interface ApiClient {
@GET
@Path("/data")
Uni<Data> getData();
}@ClientObjectMapper
public static class StrictMapper implements ObjectMapperCustomizer {
@Override
public void customize(ObjectMapper objectMapper) {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
}
}
@Path("/api")
@RegisterRestClient(configKey = "strict-api")
@RegisterProvider(StrictMapper.class)
public interface StrictApiClient {
@GET
Uni<Data> getData();
}public class SecureViewWriter implements BiFunction<ObjectMapper, Type, ObjectWriter> {
@Override
public ObjectWriter apply(ObjectMapper mapper, Type type) {
return mapper.writerWithView(Views.Secure.class);
}
}
@EnableSecureSerialization
public class SensitiveData {
@JsonView(Views.Secure.class)
private String publicInfo;
@JsonView(Views.Secure.class)
@SecureField(rolesAllowed = {"ADMIN"})
private String adminOnlyInfo;
@SecureField(rolesAllowed = {"ADMIN"})
private String adminOnlySecret;
}
@GET
@Path("/data")
@CustomSerialization(SecureViewWriter.class)
public SensitiveData getData() {
// Combines JSON views with secure field filtering
return dataService.getSensitiveData();
}public class TolerantReader implements BiFunction<ObjectMapper, Type, ObjectReader> {
@Override
public ObjectReader apply(ObjectMapper mapper, Type type) {
return mapper.reader()
.without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.without(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)
.without(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
.forType(mapper.constructType(type));
}
}
@POST
@Path("/data")
@CustomDeserialization(TolerantReader.class)
public Data createData(Data data) {
// Ignores unknown fields and null values
return dataService.create(data);
}public class ValidatingReader implements BiFunction<ObjectMapper, Type, ObjectReader> {
@Override
public ObjectReader apply(ObjectMapper mapper, Type type) {
return mapper.reader()
.with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
.with(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)
.forType(mapper.constructType(type));
}
}
@POST
@Path("/strict")
@CustomDeserialization(ValidatingReader.class)
public Data createStrict(Data data) {
// Strict validation during deserialization
return dataService.create(data);
}// Define clear view hierarchies
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
// Apply views consistently
@CustomSerialization(PublicViewWriter.class)
public User getPublicProfile();// Always use @EnableSecureSerialization with @SecureField
@EnableSecureSerialization
public class User {
@SecureField(rolesAllowed = {"ADMIN"})
private String ssn;
}public class CreateUserRequest {
@NotBlank
@Size(min = 3, max = 50)
private String username;
@NotBlank
@Email
private String email;
}
@POST
@Path("/users")
@CustomDeserialization(StrictReader.class)
public User createUser(@Valid CreateUserRequest request) {
// Combines JSON validation with Bean Validation
return userService.create(request);
}public class CachedWriter implements BiFunction<ObjectMapper, Type, ObjectWriter> {
private static final Map<Type, ObjectWriter> cache = new ConcurrentHashMap<>();
@Override
public ObjectWriter apply(ObjectMapper mapper, Type type) {
return cache.computeIfAbsent(type,
t -> mapper.writerWithView(Views.Public.class));
}
}Configure Jackson globally via application.properties:
# Include properties
quarkus.jackson.serialization-inclusion=non_null
# Date format
quarkus.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSSZ
# Time zone
quarkus.jackson.timezone=UTC
# Features
quarkus.jackson.fail-on-unknown-properties=false
quarkus.jackson.write-dates-as-timestamps=false