Jakarta Validation API defines a metadata model and API for JavaBean and method validation
—
Value extraction and validation for generic container types like collections and optionals with type-safe constraint application to container elements.
Interface for extracting values from container types for validation.
/**
* Defines how to extract values from a container type for validation
* @param <T> the container type
*/
interface ValueExtractor<T> {
/**
* Extract values from the container and pass them to the receiver
* @param originalValue the container instance
* @param receiver receives extracted values for validation
*/
void extractValues(T originalValue, ValueReceiver receiver);
/**
* Receives values extracted from containers
*/
interface ValueReceiver {
/**
* Receive a simple extracted value
* @param nodeName name for the path node (can be null)
* @param object extracted value
*/
void value(String nodeName, Object object);
/**
* Receive value from an iterable container
* @param nodeName name for the path node (can be null)
* @param object extracted value
*/
void iterableValue(String nodeName, Object object);
/**
* Receive value from an indexed container
* @param nodeName name for the path node (can be null)
* @param index index of the value
* @param object extracted value
*/
void indexedValue(String nodeName, int index, Object object);
/**
* Receive value from a keyed container (Map)
* @param nodeName name for the path node (can be null)
* @param key key of the value
* @param object extracted value
*/
void keyedValue(String nodeName, Object key, Object object);
}
}Annotations for configuring value extraction behavior.
/**
* Marks the extracted value in a ValueExtractor
* Applied to type parameters in ValueExtractor implementations
*/
@Target({TYPE_USE})
@Retention(RUNTIME)
@interface ExtractedValue {
/**
* The type of the extracted value
* @return extracted value type
*/
Class<?> type() default Object.class;
}
/**
* Marks types for default unwrapping behavior
* Applied to container types that should be automatically unwrapped
*/
@Target({TYPE})
@Retention(RUNTIME)
@interface UnwrapByDefault {}Classes for controlling value unwrapping behavior.
/**
* Unwrapping behavior configuration
*/
class Unwrapping {
/**
* Marker class indicating values should be unwrapped for validation
*/
public static class Unwrap extends Unwrapping {}
/**
* Marker class indicating values should not be unwrapped for validation
*/
public static class Skip extends Unwrapping {}
}Usage Examples:
import jakarta.validation.*;
import jakarta.validation.constraints.*;
import jakarta.validation.valueextraction.*;
import java.util.*;
// 1. Container element validation with built-in extractors
public class ContainerValidationExample {
// Validate elements in a List
private List<@NotNull @Email String> emails;
// Validate elements in a Set
private Set<@Valid @NotNull User> users;
// Validate Map keys and values
private Map<@NotBlank String, @Positive Integer> scores;
// Validate Optional content
private Optional<@NotNull @Valid User> optionalUser;
// Validate nested containers
private List<Set<@NotNull String>> nestedContainer;
}
// 2. Custom value extractor
public class CustomContainer<T> {
private T value;
public T getValue() { return value; }
public void setValue(T value) { this.value = value; }
}
// Value extractor for CustomContainer
public class CustomContainerValueExtractor
implements ValueExtractor<CustomContainer<@ExtractedValue ?>> {
@Override
public void extractValues(CustomContainer<?> originalValue, ValueReceiver receiver) {
if (originalValue != null) {
receiver.value("value", originalValue.getValue());
}
}
}
// 3. Register custom extractor in configuration
public class CustomValidationConfiguration {
public Validator createValidator() {
Configuration<?> config = Validation.byDefaultProvider().configure();
config.addValueExtractor(new CustomContainerValueExtractor());
ValidatorFactory factory = config.buildValidatorFactory();
return factory.getValidator();
}
}
// 4. Usage with custom container
public class MyService {
@Valid
private CustomContainer<@NotNull @Email String> containerizedEmail;
@Valid
private CustomContainer<@Valid User> containerizedUser;
}
// 5. Complex container validation
public class ComplexContainerExample {
// Nested generics with constraints
private Map<@NotBlank String, List<@Valid @NotNull Product>> productsByCategory;
// Optional with nested container
private Optional<List<@NotNull @Size(min=1, max=100) String>> optionalStringList;
// Custom container with validation
@Valid
private CustomContainer<List<@Email String>> emailContainer;
}
// 6. Validation example
public class ContainerValidationDemo {
private Validator validator;
public ContainerValidationDemo() {
// Create validator with custom extractor
Configuration<?> config = Validation.byDefaultProvider().configure();
config.addValueExtractor(new CustomContainerValueExtractor());
ValidatorFactory factory = config.buildValidatorFactory();
this.validator = factory.getValidator();
}
public void validateContainers() {
// Create test object with invalid container elements
ContainerValidationExample example = new ContainerValidationExample();
// List with null and invalid emails
example.setEmails(Arrays.asList(null, "invalid-email", "valid@example.com"));
// Map with blank key and negative value
Map<String, Integer> scores = new HashMap<>();
scores.put("", -5); // Invalid key and value
scores.put("valid-key", 100); // Valid entry
example.setScores(scores);
// Validate
Set<ConstraintViolation<ContainerValidationExample>> violations =
validator.validate(example);
for (ConstraintViolation<ContainerValidationExample> violation : violations) {
System.out.println("Path: " + violation.getPropertyPath());
System.out.println("Message: " + violation.getMessage());
System.out.println("Invalid value: " + violation.getInvalidValue());
System.out.println("---");
}
// Expected output shows path like:
// emails[0] - NotNull violation
// emails[1] - Email violation
// scores<K>[].key - NotBlank violation
// scores<>[].value - Positive violation
}
}Install with Tessl CLI
npx tessl i tessl/maven-jakarta-validation--jakarta-validation-api