or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bean-validation.mdbootstrap-configuration.mdconstraints.mdcontainer-validation.mdcustom-constraints.mdindex.mdmetadata.mdmethod-validation.mdvalidation-groups.md

container-validation.mddocs/

0

# Container Element Validation

1

2

Value extraction and validation for generic container types like collections and optionals with type-safe constraint application to container elements.

3

4

## Capabilities

5

6

### Value Extractor

7

8

Interface for extracting values from container types for validation.

9

10

```java { .api }

11

/**

12

* Defines how to extract values from a container type for validation

13

* @param <T> the container type

14

*/

15

interface ValueExtractor<T> {

16

/**

17

* Extract values from the container and pass them to the receiver

18

* @param originalValue the container instance

19

* @param receiver receives extracted values for validation

20

*/

21

void extractValues(T originalValue, ValueReceiver receiver);

22

23

/**

24

* Receives values extracted from containers

25

*/

26

interface ValueReceiver {

27

/**

28

* Receive a simple extracted value

29

* @param nodeName name for the path node (can be null)

30

* @param object extracted value

31

*/

32

void value(String nodeName, Object object);

33

34

/**

35

* Receive value from an iterable container

36

* @param nodeName name for the path node (can be null)

37

* @param object extracted value

38

*/

39

void iterableValue(String nodeName, Object object);

40

41

/**

42

* Receive value from an indexed container

43

* @param nodeName name for the path node (can be null)

44

* @param index index of the value

45

* @param object extracted value

46

*/

47

void indexedValue(String nodeName, int index, Object object);

48

49

/**

50

* Receive value from a keyed container (Map)

51

* @param nodeName name for the path node (can be null)

52

* @param key key of the value

53

* @param object extracted value

54

*/

55

void keyedValue(String nodeName, Object key, Object object);

56

}

57

}

58

```

59

60

### Value Extraction Annotations

61

62

Annotations for configuring value extraction behavior.

63

64

```java { .api }

65

/**

66

* Marks the extracted value in a ValueExtractor

67

* Applied to type parameters in ValueExtractor implementations

68

*/

69

@Target({TYPE_USE})

70

@Retention(RUNTIME)

71

@interface ExtractedValue {

72

/**

73

* The type of the extracted value

74

* @return extracted value type

75

*/

76

Class<?> type() default Object.class;

77

}

78

79

/**

80

* Marks types for default unwrapping behavior

81

* Applied to container types that should be automatically unwrapped

82

*/

83

@Target({TYPE})

84

@Retention(RUNTIME)

85

@interface UnwrapByDefault {}

86

```

87

88

### Unwrapping Configuration

89

90

Classes for controlling value unwrapping behavior.

91

92

```java { .api }

93

/**

94

* Unwrapping behavior configuration

95

*/

96

class Unwrapping {

97

/**

98

* Marker class indicating values should be unwrapped for validation

99

*/

100

public static class Unwrap extends Unwrapping {}

101

102

/**

103

* Marker class indicating values should not be unwrapped for validation

104

*/

105

public static class Skip extends Unwrapping {}

106

}

107

```

108

109

**Usage Examples:**

110

111

```java

112

import jakarta.validation.*;

113

import jakarta.validation.constraints.*;

114

import jakarta.validation.valueextraction.*;

115

import java.util.*;

116

117

// 1. Container element validation with built-in extractors

118

public class ContainerValidationExample {

119

// Validate elements in a List

120

private List<@NotNull @Email String> emails;

121

122

// Validate elements in a Set

123

private Set<@Valid @NotNull User> users;

124

125

// Validate Map keys and values

126

private Map<@NotBlank String, @Positive Integer> scores;

127

128

// Validate Optional content

129

private Optional<@NotNull @Valid User> optionalUser;

130

131

// Validate nested containers

132

private List<Set<@NotNull String>> nestedContainer;

133

}

134

135

// 2. Custom value extractor

136

public class CustomContainer<T> {

137

private T value;

138

139

public T getValue() { return value; }

140

public void setValue(T value) { this.value = value; }

141

}

142

143

// Value extractor for CustomContainer

144

public class CustomContainerValueExtractor

145

implements ValueExtractor<CustomContainer<@ExtractedValue ?>> {

146

147

@Override

148

public void extractValues(CustomContainer<?> originalValue, ValueReceiver receiver) {

149

if (originalValue != null) {

150

receiver.value("value", originalValue.getValue());

151

}

152

}

153

}

154

155

// 3. Register custom extractor in configuration

156

public class CustomValidationConfiguration {

157

public Validator createValidator() {

158

Configuration<?> config = Validation.byDefaultProvider().configure();

159

config.addValueExtractor(new CustomContainerValueExtractor());

160

161

ValidatorFactory factory = config.buildValidatorFactory();

162

return factory.getValidator();

163

}

164

}

165

166

// 4. Usage with custom container

167

public class MyService {

168

@Valid

169

private CustomContainer<@NotNull @Email String> containerizedEmail;

170

171

@Valid

172

private CustomContainer<@Valid User> containerizedUser;

173

}

174

175

// 5. Complex container validation

176

public class ComplexContainerExample {

177

// Nested generics with constraints

178

private Map<@NotBlank String, List<@Valid @NotNull Product>> productsByCategory;

179

180

// Optional with nested container

181

private Optional<List<@NotNull @Size(min=1, max=100) String>> optionalStringList;

182

183

// Custom container with validation

184

@Valid

185

private CustomContainer<List<@Email String>> emailContainer;

186

}

187

188

// 6. Validation example

189

public class ContainerValidationDemo {

190

private Validator validator;

191

192

public ContainerValidationDemo() {

193

// Create validator with custom extractor

194

Configuration<?> config = Validation.byDefaultProvider().configure();

195

config.addValueExtractor(new CustomContainerValueExtractor());

196

ValidatorFactory factory = config.buildValidatorFactory();

197

this.validator = factory.getValidator();

198

}

199

200

public void validateContainers() {

201

// Create test object with invalid container elements

202

ContainerValidationExample example = new ContainerValidationExample();

203

204

// List with null and invalid emails

205

example.setEmails(Arrays.asList(null, "invalid-email", "valid@example.com"));

206

207

// Map with blank key and negative value

208

Map<String, Integer> scores = new HashMap<>();

209

scores.put("", -5); // Invalid key and value

210

scores.put("valid-key", 100); // Valid entry

211

example.setScores(scores);

212

213

// Validate

214

Set<ConstraintViolation<ContainerValidationExample>> violations =

215

validator.validate(example);

216

217

for (ConstraintViolation<ContainerValidationExample> violation : violations) {

218

System.out.println("Path: " + violation.getPropertyPath());

219

System.out.println("Message: " + violation.getMessage());

220

System.out.println("Invalid value: " + violation.getInvalidValue());

221

System.out.println("---");

222

}

223

224

// Expected output shows path like:

225

// emails[0] - NotNull violation

226

// emails[1] - Email violation

227

// scores<K>[].key - NotBlank violation

228

// scores<>[].value - Positive violation

229

}

230

}

231

```