or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconstraint-validation.mdconstraints.mdindex.mdmessage-interpolation.mdpath-navigation.mdprogrammatic-config.mdresource-loading.mdspi.md

path-navigation.mddocs/

0

# Property Path Navigation

1

2

Hibernate-specific extensions to Jakarta Validation property path nodes providing access to actual property and container element values during validation for enhanced error reporting and debugging.

3

4

## Capabilities

5

6

### Property Node with Value Access

7

8

Property node extension providing access to the actual property value.

9

10

```java { .api }

11

package org.hibernate.validator.path;

12

13

import jakarta.validation.Path;

14

15

/**

16

* Node representing a property with Hibernate Validator specific functionality.

17

* Extends standard Jakarta Validation PropertyNode with value access and additional

18

* Hibernate-specific container metadata.

19

*/

20

interface PropertyNode extends Path.PropertyNode {

21

/**

22

* Get the value of the bean property represented by this node.

23

* Returns the actual value that was validated.

24

*

25

* @return property value or null if not available

26

*/

27

Object getValue();

28

29

/**

30

* Get the container class (e.g., List, Map, Optional) of this property node.

31

* Hibernate-specific extension.

32

*

33

* @return container class or null if not a container element

34

* @since 6.0

35

*/

36

@Incubating

37

Class<?> getContainerClass();

38

39

/**

40

* Get the type argument index in the container's generic type declaration.

41

* Hibernate-specific extension.

42

*

43

* @return type argument index or null if not a container element

44

* @since 6.0

45

*/

46

@Incubating

47

Integer getTypeArgumentIndex();

48

49

// Inherited from Path.PropertyNode:

50

// String getName()

51

// Integer getIndex()

52

// Object getKey()

53

// ElementKind getKind()

54

// boolean isInIterable()

55

}

56

```

57

58

**Usage Example:**

59

60

```java

61

import org.hibernate.validator.path.PropertyNode;

62

import jakarta.validation.*;

63

import java.util.Set;

64

65

class Product {

66

@Min(10)

67

private int quantity;

68

69

@NotNull

70

private String name;

71

72

// getters/setters...

73

}

74

75

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

76

77

Product product = new Product();

78

product.setQuantity(5); // Invalid

79

product.setName(null); // Invalid

80

81

Set<ConstraintViolation<Product>> violations = validator.validate(product);

82

83

for (ConstraintViolation<Product> violation : violations) {

84

Path propertyPath = violation.getPropertyPath();

85

86

for (Path.Node node : propertyPath) {

87

if (node.getKind() == ElementKind.PROPERTY) {

88

// Cast to Hibernate PropertyNode

89

PropertyNode propertyNode = node.as(PropertyNode.class);

90

91

System.out.println("Property: " + propertyNode.getName());

92

System.out.println("Invalid value: " + propertyNode.getValue());

93

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

94

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

95

}

96

}

97

}

98

99

// Output:

100

// Property: quantity

101

// Invalid value: 5

102

// Message: must be greater than or equal to 10

103

// ---

104

// Property: name

105

// Invalid value: null

106

// Message: must not be null

107

// ---

108

```

109

110

### Container Element Node with Value Access

111

112

Container element node extension providing access to the actual container element value.

113

114

```java { .api }

115

package org.hibernate.validator.path;

116

117

import jakarta.validation.Path;

118

119

/**

120

* Node representing a container element with Hibernate Validator specific functionality.

121

* Extends standard Jakarta Validation ContainerElementNode with value access and additional

122

* Hibernate-specific container metadata.

123

*/

124

interface ContainerElementNode extends Path.ContainerElementNode {

125

/**

126

* Get the value of the container element represented by this node.

127

* Returns the actual element value from the container.

128

*

129

* @return container element value or null if not available

130

*/

131

Object getValue();

132

133

/**

134

* Get the container class (e.g., List, Map, Optional) of this container element node.

135

* Hibernate-specific extension.

136

*

137

* @return container class or null if not available

138

* @since 6.0

139

*/

140

@Incubating

141

Class<?> getContainerClass();

142

143

/**

144

* Get the type argument index in the container's generic type declaration.

145

* Hibernate-specific extension.

146

*

147

* @return type argument index or null if not available

148

* @since 6.0

149

*/

150

@Incubating

151

Integer getTypeArgumentIndex();

152

153

// Inherited from Path.ContainerElementNode:

154

// String getName()

155

// Integer getIndex()

156

// Object getKey()

157

// ElementKind getKind()

158

// boolean isInIterable()

159

}

160

```

161

162

**Usage Example:**

163

164

```java

165

import org.hibernate.validator.path.ContainerElementNode;

166

import jakarta.validation.*;

167

import jakarta.validation.constraints.*;

168

import java.util.*;

169

170

class ShoppingCart {

171

@NotNull

172

@Size(min = 1)

173

private List<@NotNull @Min(1) Integer> itemQuantities;

174

175

public ShoppingCart(List<Integer> itemQuantities) {

176

this.itemQuantities = itemQuantities;

177

}

178

179

// getters/setters...

180

}

181

182

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

183

184

// Create cart with invalid data

185

ShoppingCart cart = new ShoppingCart(Arrays.asList(1, 0, 5, null, -2));

186

187

Set<ConstraintViolation<ShoppingCart>> violations = validator.validate(cart);

188

189

for (ConstraintViolation<ShoppingCart> violation : violations) {

190

Path propertyPath = violation.getPropertyPath();

191

192

for (Path.Node node : propertyPath) {

193

if (node.getKind() == ElementKind.CONTAINER_ELEMENT) {

194

// Cast to Hibernate ContainerElementNode

195

ContainerElementNode containerNode = node.as(ContainerElementNode.class);

196

197

System.out.println("Container: " + containerNode.getContainerClass().getSimpleName());

198

System.out.println("Index: " + containerNode.getIndex());

199

System.out.println("Invalid element value: " + containerNode.getValue());

200

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

201

System.out.println("Full path: " + propertyPath);

202

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

203

}

204

}

205

}

206

207

// Output includes:

208

// Container: List

209

// Index: 1

210

// Invalid element value: 0

211

// Message: must be greater than or equal to 1

212

// Full path: itemQuantities[1].<list element>

213

// ---

214

// Container: List

215

// Index: 3

216

// Invalid element value: null

217

// Message: must not be null

218

// Full path: itemQuantities[3].<list element>

219

// ---

220

```

221

222

## Complete Property Path Navigation Example

223

224

```java

225

import org.hibernate.validator.path.PropertyNode;

226

import org.hibernate.validator.path.ContainerElementNode;

227

import jakarta.validation.*;

228

import jakarta.validation.constraints.*;

229

import java.util.*;

230

231

// Complex nested structure

232

class Order {

233

@Valid

234

private Customer customer;

235

236

@NotNull

237

@Size(min = 1)

238

private List<@Valid OrderItem> items;

239

240

private Map<@NotBlank String, @Valid @NotNull Address> deliveryAddresses;

241

242

// getters/setters...

243

}

244

245

class Customer {

246

@NotBlank

247

private String name;

248

249

@Email

250

private String email;

251

252

// getters/setters...

253

}

254

255

class OrderItem {

256

@NotNull

257

private String productId;

258

259

@Min(1)

260

private int quantity;

261

262

@DecimalMin("0.01")

263

private BigDecimal price;

264

265

// getters/setters...

266

}

267

268

class Address {

269

@NotBlank

270

private String street;

271

272

@NotBlank

273

private String city;

274

275

// getters/setters...

276

}

277

278

// Validate and navigate paths

279

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

280

281

Order order = new Order();

282

// ... set up invalid data ...

283

284

Set<ConstraintViolation<Order>> violations = validator.validate(order);

285

286

for (ConstraintViolation<Order> violation : violations) {

287

System.out.println("\n=== Constraint Violation ===");

288

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

289

System.out.println("Root bean: " + violation.getRootBeanClass().getSimpleName());

290

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

291

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

292

293

System.out.println("\nPath details:");

294

295

for (Path.Node node : violation.getPropertyPath()) {

296

System.out.println(" Node kind: " + node.getKind());

297

System.out.println(" Node name: " + node.getName());

298

299

switch (node.getKind()) {

300

case PROPERTY:

301

PropertyNode propNode = node.as(PropertyNode.class);

302

System.out.println(" Property value: " + propNode.getValue());

303

break;

304

305

case CONTAINER_ELEMENT:

306

ContainerElementNode containerNode = node.as(ContainerElementNode.class);

307

System.out.println(" Container class: " +

308

containerNode.getContainerClass().getSimpleName());

309

System.out.println(" Element index: " + containerNode.getIndex());

310

System.out.println(" Element key: " + containerNode.getKey());

311

System.out.println(" Element value: " + containerNode.getValue());

312

System.out.println(" Type argument index: " +

313

containerNode.getTypeArgumentIndex());

314

break;

315

316

case BEAN:

317

System.out.println(" Bean node");

318

break;

319

320

case PARAMETER:

321

System.out.println(" Parameter index: " + node.as(Path.ParameterNode.class).getParameterIndex());

322

break;

323

324

case RETURN_VALUE:

325

System.out.println(" Return value node");

326

break;

327

}

328

329

if (node.isInIterable()) {

330

System.out.println(" In iterable: true");

331

System.out.println(" Index: " + node.getIndex());

332

System.out.println(" Key: " + node.getKey());

333

}

334

335

System.out.println();

336

}

337

}

338

339

// Example output for items[1].quantity violation:

340

// === Constraint Violation ===

341

// Message: must be greater than or equal to 1

342

// Root bean: Order

343

// Property path: items[1].quantity

344

// Invalid value: 0

345

//

346

// Path details:

347

// Node kind: PROPERTY

348

// Node name: items

349

// Property value: [OrderItem@123, OrderItem@456]

350

//

351

// Node kind: CONTAINER_ELEMENT

352

// Node name: <list element>

353

// Container class: List

354

// Element index: 1

355

// Element key: null

356

// Element value: OrderItem@456

357

// Type argument index: 0

358

//

359

// Node kind: PROPERTY

360

// Node name: quantity

361

// Property value: 0

362

```

363

364

## Path Navigation for Method Validation

365

366

```java

367

import org.hibernate.validator.path.PropertyNode;

368

import jakarta.validation.*;

369

import jakarta.validation.executable.ExecutableValidator;

370

import java.lang.reflect.Method;

371

import java.util.Set;

372

373

class UserService {

374

public User createUser(

375

@NotBlank String username,

376

@Email String email,

377

@Min(18) int age

378

) {

379

return new User(username, email, age);

380

}

381

382

@Valid

383

@NotNull

384

public User getUser(@NotNull String id) {

385

return new User("test", "test@example.com", 25);

386

}

387

}

388

389

// Validate method parameters

390

ExecutableValidator execValidator = validator.forExecutables();

391

UserService service = new UserService();

392

393

try {

394

Method method = UserService.class.getMethod("createUser",

395

String.class, String.class, int.class);

396

397

Object[] parameters = {"", "invalid-email", 15}; // Invalid parameters

398

399

Set<ConstraintViolation<UserService>> violations =

400

execValidator.validateParameters(service, method, parameters);

401

402

for (ConstraintViolation<UserService> violation : violations) {

403

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

404

405

for (Path.Node node : violation.getPropertyPath()) {

406

if (node.getKind() == ElementKind.METHOD) {

407

System.out.println("Method: " + node.getName());

408

} else if (node.getKind() == ElementKind.PARAMETER) {

409

Path.ParameterNode paramNode = node.as(Path.ParameterNode.class);

410

System.out.println("Parameter index: " + paramNode.getParameterIndex());

411

System.out.println("Parameter name: " + paramNode.getName());

412

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

413

}

414

}

415

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

416

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

417

}

418

} catch (NoSuchMethodException e) {

419

e.printStackTrace();

420

}

421

422

// Output:

423

// Violation on: createUser.arg0

424

// Method: createUser

425

// Parameter index: 0

426

// Parameter name: arg0

427

// Invalid value:

428

// Message: must not be blank

429

// ---

430

// Violation on: createUser.arg1

431

// Method: createUser

432

// Parameter index: 1

433

// Parameter name: arg1

434

// Invalid value: invalid-email

435

// Message: must be a well-formed email address

436

// ---

437

```

438

439

## Accessing Values for Custom Error Reporting

440

441

```java

442

import org.hibernate.validator.path.PropertyNode;

443

import org.hibernate.validator.path.ContainerElementNode;

444

import jakarta.validation.*;

445

import java.util.*;

446

447

/**

448

* Custom error reporter that extracts detailed information from property paths.

449

*/

450

class DetailedErrorReporter {

451

452

public List<ErrorDetail> generateErrorReport(Set<ConstraintViolation<?>> violations) {

453

List<ErrorDetail> errors = new ArrayList<>();

454

455

for (ConstraintViolation<?> violation : violations) {

456

ErrorDetail detail = new ErrorDetail();

457

detail.setMessage(violation.getMessage());

458

detail.setInvalidValue(violation.getInvalidValue());

459

detail.setPropertyPath(violation.getPropertyPath().toString());

460

461

// Extract detailed path information

462

List<PathSegment> pathSegments = new ArrayList<>();

463

464

for (Path.Node node : violation.getPropertyPath()) {

465

PathSegment segment = new PathSegment();

466

segment.setKind(node.getKind().name());

467

segment.setName(node.getName());

468

469

// Get actual value for properties and container elements

470

if (node.getKind() == ElementKind.PROPERTY) {

471

PropertyNode propNode = node.as(PropertyNode.class);

472

segment.setValue(propNode.getValue());

473

} else if (node.getKind() == ElementKind.CONTAINER_ELEMENT) {

474

ContainerElementNode containerNode = node.as(ContainerElementNode.class);

475

segment.setValue(containerNode.getValue());

476

segment.setContainerType(containerNode.getContainerClass().getSimpleName());

477

segment.setIndex(containerNode.getIndex());

478

segment.setKey(containerNode.getKey());

479

}

480

481

pathSegments.add(segment);

482

}

483

484

detail.setPathSegments(pathSegments);

485

errors.add(detail);

486

}

487

488

return errors;

489

}

490

}

491

492

class ErrorDetail {

493

private String message;

494

private Object invalidValue;

495

private String propertyPath;

496

private List<PathSegment> pathSegments;

497

498

// getters/setters...

499

}

500

501

class PathSegment {

502

private String kind;

503

private String name;

504

private Object value;

505

private String containerType;

506

private Integer index;

507

private Object key;

508

509

// getters/setters...

510

}

511

512

// Usage

513

DetailedErrorReporter reporter = new DetailedErrorReporter();

514

Set<ConstraintViolation<Order>> violations = validator.validate(order);

515

List<ErrorDetail> errors = reporter.generateErrorReport(violations);

516

517

// Convert to JSON, log, display in UI, etc.

518

```

519