or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-resteasy-reactive@3.15.x

docs

index.md
tile.json

tessl/maven-io-quarkus--quarkus-resteasy-reactive

tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0

A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.

jackson-integration.mddocs/reference/

Jackson Integration

Quarkus REST provides deep Jackson integration for JSON serialization and deserialization, including method-level customization, security-aware field filtering, and custom ObjectMapper configuration.

Import

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;

Annotations

@CustomSerialization

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();
}

@CustomDeserialization

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();
}

@SecureField

Marks fields for role-based serialization filtering.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecureField {
    /**
     * Roles allowed to see this field
     */
    String[] rolesAllowed();
}

@EnableSecureSerialization

Enables secure serialization for a resource method or class.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableSecureSerialization {
}

@DisableSecureSerialization

Disables secure serialization for a resource method or class.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisableSecureSerialization {
}

@ClientObjectMapper (REST Client)

Registers a custom ObjectMapper provider for REST Client.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientObjectMapper {
}

Custom Serialization

Basic Custom Serialization

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);
}

JSON Views

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" }
    }
}

Custom Filters

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();
}

Custom Deserialization

Basic Custom Deserialization

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);
}

Custom Date Handling

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);
}

Polymorphic Deserialization

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);
}

Secure Field Serialization

Basic Secure Fields

@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);
}

Class-Level Secure Serialization

@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);
    }
}

Multiple Role Requirements

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;
}

Nested Secure Fields

@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;
}

Client ObjectMapper

Custom Client ObjectMapper

@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();
}

Per-Client Customization

@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();
}

Combining Annotations

Custom Serialization with Secure Fields

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();
}

Error Handling

Deserialization Errors

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);
}

Validation

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);
}

Best Practices

Use Views for Public APIs

// 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();

Secure Fields for Sensitive Data

// Always use @EnableSecureSerialization with @SecureField
@EnableSecureSerialization
public class User {
    @SecureField(rolesAllowed = {"ADMIN"})
    private String ssn;
}

Combine with Bean Validation

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);
}

Cache ObjectWriter/ObjectReader

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));
    }
}

Configuration

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