0
# Validation and Error Handling
1
2
Jakarta XML Binding provides a comprehensive event-driven validation framework with detailed error reporting, location tracking, and customizable event handling for XML binding operations. This framework enables robust error handling and validation during marshalling and unmarshalling processes.
3
4
## Capabilities
5
6
### ValidationEvent Framework
7
8
The core validation event system provides structured error reporting with severity levels and location information.
9
10
```java { .api }
11
public interface ValidationEvent {
12
// Severity constants
13
int WARNING = 0;
14
int ERROR = 1;
15
int FATAL_ERROR = 2;
16
17
// Event information
18
int getSeverity();
19
String getMessage();
20
Throwable getLinkedException();
21
ValidationEventLocator getLocator();
22
}
23
24
public interface ValidationEventHandler {
25
boolean handleEvent(ValidationEvent event);
26
}
27
28
public interface ValidationEventLocator {
29
java.net.URL getURL();
30
int getOffset();
31
int getLineNumber();
32
int getColumnNumber();
33
Object getObject();
34
org.w3c.dom.Node getNode();
35
}
36
```
37
38
**Usage Examples:**
39
40
```java
41
// Custom validation event handler
42
public class CustomValidationEventHandler implements ValidationEventHandler {
43
@Override
44
public boolean handleEvent(ValidationEvent event) {
45
String severity = getSeverityString(event.getSeverity());
46
String message = event.getMessage();
47
ValidationEventLocator locator = event.getLocator();
48
49
System.err.printf("[%s] %s%n", severity, message);
50
51
if (locator != null) {
52
System.err.printf(" Location: Line %d, Column %d%n",
53
locator.getLineNumber(),
54
locator.getColumnNumber()
55
);
56
57
if (locator.getURL() != null) {
58
System.err.printf(" URL: %s%n", locator.getURL());
59
}
60
}
61
62
// Handle linked exception
63
Throwable cause = event.getLinkedException();
64
if (cause != null) {
65
System.err.printf(" Caused by: %s%n", cause.getMessage());
66
}
67
68
// Continue processing for warnings and errors, stop for fatal errors
69
return event.getSeverity() != ValidationEvent.FATAL_ERROR;
70
}
71
72
private String getSeverityString(int severity) {
73
switch (severity) {
74
case ValidationEvent.WARNING: return "WARNING";
75
case ValidationEvent.ERROR: return "ERROR";
76
case ValidationEvent.FATAL_ERROR: return "FATAL";
77
default: return "UNKNOWN";
78
}
79
}
80
}
81
82
// Apply to marshaller/unmarshaller
83
JAXBContext context = JAXBContext.newInstance(Person.class);
84
Unmarshaller unmarshaller = context.createUnmarshaller();
85
unmarshaller.setEventHandler(new CustomValidationEventHandler());
86
87
// Validation events will be sent to custom handler
88
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
89
```
90
91
### Specialized Validation Events
92
93
Jakarta XML Binding defines specialized validation event types for specific error scenarios.
94
95
```java { .api }
96
public interface ParseConversionEvent extends ValidationEvent {
97
// Inherits all ValidationEvent methods
98
// Represents errors during parsing/conversion from string to Java types
99
}
100
101
public interface PrintConversionEvent extends ValidationEvent {
102
// Inherits all ValidationEvent methods
103
// Represents errors during formatting/conversion from Java types to string
104
}
105
106
public interface NotIdentifiableEvent extends ValidationEvent {
107
// Inherits all ValidationEvent methods
108
// Represents errors when objects cannot be identified for marshalling
109
}
110
```
111
112
**Event Type Usage:**
113
114
```java
115
public class TypedValidationEventHandler implements ValidationEventHandler {
116
@Override
117
public boolean handleEvent(ValidationEvent event) {
118
if (event instanceof ParseConversionEvent) {
119
handleParseError((ParseConversionEvent) event);
120
} else if (event instanceof PrintConversionEvent) {
121
handlePrintError((PrintConversionEvent) event);
122
} else if (event instanceof NotIdentifiableEvent) {
123
handleIdentificationError((NotIdentifiableEvent) event);
124
} else {
125
handleGenericError(event);
126
}
127
128
// Continue processing unless fatal
129
return event.getSeverity() != ValidationEvent.FATAL_ERROR;
130
}
131
132
private void handleParseError(ParseConversionEvent event) {
133
System.err.println("Parse conversion error: " + event.getMessage());
134
// Log parsing-specific information
135
}
136
137
private void handlePrintError(PrintConversionEvent event) {
138
System.err.println("Print conversion error: " + event.getMessage());
139
// Log formatting-specific information
140
}
141
142
private void handleIdentificationError(NotIdentifiableEvent event) {
143
System.err.println("Object identification error: " + event.getMessage());
144
// Handle unidentifiable object scenarios
145
}
146
147
private void handleGenericError(ValidationEvent event) {
148
System.err.println("Validation error: " + event.getMessage());
149
}
150
}
151
```
152
153
### Built-in Event Handler Implementations
154
155
Jakarta XML Binding provides default implementations for common validation scenarios.
156
157
```java { .api }
158
// In jakarta.xml.bind.helpers package
159
public class DefaultValidationEventHandler implements ValidationEventHandler {
160
public boolean handleEvent(ValidationEvent event);
161
}
162
163
public class ValidationEventCollector implements ValidationEventHandler {
164
public ValidationEvent[] getEvents();
165
public boolean hasEvents();
166
public void reset();
167
public boolean handleEvent(ValidationEvent event);
168
}
169
```
170
171
**Usage Examples:**
172
173
```java
174
// Default handler (terminates on first error)
175
Unmarshaller unmarshaller = context.createUnmarshaller();
176
unmarshaller.setEventHandler(new DefaultValidationEventHandler());
177
178
// Event collector (collects all events for later review)
179
ValidationEventCollector eventCollector = new ValidationEventCollector();
180
unmarshaller.setEventHandler(eventCollector);
181
182
try {
183
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
184
185
// Check collected events after processing
186
if (eventCollector.hasEvents()) {
187
ValidationEvent[] events = eventCollector.getEvents();
188
189
System.out.printf("Processing completed with %d validation events:%n", events.length);
190
191
for (ValidationEvent event : events) {
192
System.err.printf(" [%s] %s%n",
193
getSeverityString(event.getSeverity()),
194
event.getMessage()
195
);
196
}
197
}
198
199
} catch (JAXBException e) {
200
System.err.println("Fatal error during unmarshalling: " + e.getMessage());
201
}
202
203
// Reset collector for reuse
204
eventCollector.reset();
205
```
206
207
### Schema Validation Integration
208
209
Jakarta XML Binding integrates with javax.xml.validation for XML Schema validation during binding operations.
210
211
```java { .api }
212
// In Marshaller and Unmarshaller interfaces
213
public interface Marshaller {
214
void setSchema(javax.xml.validation.Schema schema);
215
javax.xml.validation.Schema getSchema();
216
}
217
218
public interface Unmarshaller {
219
void setSchema(javax.xml.validation.Schema schema);
220
javax.xml.validation.Schema getSchema();
221
}
222
```
223
224
**Schema Validation Examples:**
225
226
```java
227
// Load XML Schema
228
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
229
Schema schema = schemaFactory.newSchema(new File("person.xsd"));
230
231
// Set up validation event handler
232
ValidationEventCollector eventCollector = new ValidationEventCollector();
233
234
// Configure unmarshaller with schema validation
235
JAXBContext context = JAXBContext.newInstance(Person.class);
236
Unmarshaller unmarshaller = context.createUnmarshaller();
237
unmarshaller.setSchema(schema);
238
unmarshaller.setEventHandler(eventCollector);
239
240
try {
241
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
242
243
// Check for schema validation errors
244
if (eventCollector.hasEvents()) {
245
for (ValidationEvent event : eventCollector.getEvents()) {
246
System.err.printf("Schema validation error: %s%n", event.getMessage());
247
}
248
} else {
249
System.out.println("Document is valid according to schema");
250
}
251
252
} catch (JAXBException e) {
253
System.err.println("Unmarshalling failed: " + e.getMessage());
254
}
255
256
// Configure marshaller with schema validation
257
Marshaller marshaller = context.createMarshaller();
258
marshaller.setSchema(schema);
259
marshaller.setEventHandler(eventCollector);
260
261
eventCollector.reset();
262
try {
263
marshaller.marshal(person, System.out);
264
265
if (eventCollector.hasEvents()) {
266
System.err.println("Generated XML does not conform to schema");
267
}
268
} catch (JAXBException e) {
269
System.err.println("Marshalling failed: " + e.getMessage());
270
}
271
```
272
273
### Exception Hierarchy
274
275
Jakarta XML Binding defines a comprehensive exception hierarchy for different error scenarios.
276
277
```java { .api }
278
public class JAXBException extends Exception {
279
public JAXBException(String message);
280
public JAXBException(String message, String errorCode);
281
public JAXBException(Throwable exception);
282
public JAXBException(String message, Throwable exception);
283
public JAXBException(String message, String errorCode, Throwable exception);
284
285
public String getErrorCode();
286
public Throwable getLinkedException();
287
public void setLinkedException(Throwable exception);
288
}
289
290
public class MarshalException extends JAXBException {
291
// Same constructors as JAXBException
292
// Thrown during marshalling operations
293
}
294
295
public class UnmarshalException extends JAXBException {
296
// Same constructors as JAXBException
297
// Thrown during unmarshalling operations
298
}
299
300
public class ValidationException extends JAXBException {
301
// Same constructors as JAXBException
302
// Thrown during validation operations
303
}
304
305
public class PropertyException extends JAXBException {
306
// Same constructors as JAXBException
307
// Thrown for property-related errors
308
}
309
310
public class DataBindingException extends RuntimeException {
311
public DataBindingException(String message, Throwable cause);
312
public DataBindingException(Throwable cause);
313
// Runtime exception used by convenience JAXB class
314
}
315
316
public class TypeConstraintException extends RuntimeException {
317
public TypeConstraintException(String message);
318
public TypeConstraintException(String message, Throwable cause);
319
public TypeConstraintException(Throwable cause);
320
// Runtime exception for type constraint violations
321
}
322
```
323
324
**Exception Handling Examples:**
325
326
```java
327
// Comprehensive exception handling
328
public class RobustXMLProcessor {
329
330
public Person loadPerson(File xmlFile) {
331
try {
332
JAXBContext context = JAXBContext.newInstance(Person.class);
333
Unmarshaller unmarshaller = context.createUnmarshaller();
334
335
return (Person) unmarshaller.unmarshal(xmlFile);
336
337
} catch (UnmarshalException e) {
338
System.err.println("Failed to unmarshal XML: " + e.getMessage());
339
340
// Check for linked exceptions
341
Throwable cause = e.getLinkedException();
342
if (cause instanceof SAXParseException) {
343
SAXParseException saxError = (SAXParseException) cause;
344
System.err.printf("XML parsing error at line %d, column %d: %s%n",
345
saxError.getLineNumber(),
346
saxError.getColumnNumber(),
347
saxError.getMessage()
348
);
349
}
350
351
return null;
352
353
} catch (ValidationException e) {
354
System.err.println("Validation error: " + e.getMessage());
355
return null;
356
357
} catch (JAXBException e) {
358
System.err.println("JAXB error: " + e.getMessage());
359
360
String errorCode = e.getErrorCode();
361
if (errorCode != null) {
362
System.err.println("Error code: " + errorCode);
363
}
364
365
return null;
366
}
367
}
368
369
public boolean savePerson(Person person, File xmlFile) {
370
try {
371
JAXBContext context = JAXBContext.newInstance(Person.class);
372
Marshaller marshaller = context.createMarshaller();
373
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
374
375
marshaller.marshal(person, xmlFile);
376
return true;
377
378
} catch (MarshalException e) {
379
System.err.println("Failed to marshal object: " + e.getMessage());
380
return false;
381
382
} catch (PropertyException e) {
383
System.err.println("Property configuration error: " + e.getMessage());
384
return false;
385
386
} catch (JAXBException e) {
387
System.err.println("JAXB error: " + e.getMessage());
388
return false;
389
}
390
}
391
}
392
```
393
394
### Custom Error Reporting
395
396
Advanced error handling with detailed reporting and recovery strategies.
397
398
```java
399
// Advanced validation event handler with detailed reporting
400
public class DetailedValidationEventHandler implements ValidationEventHandler {
401
private final List<ValidationEvent> warnings = new ArrayList<>();
402
private final List<ValidationEvent> errors = new ArrayList<>();
403
private final List<ValidationEvent> fatalErrors = new ArrayList<>();
404
405
@Override
406
public boolean handleEvent(ValidationEvent event) {
407
// Categorize events by severity
408
switch (event.getSeverity()) {
409
case ValidationEvent.WARNING:
410
warnings.add(event);
411
logEvent("WARNING", event);
412
return true; // Continue processing
413
414
case ValidationEvent.ERROR:
415
errors.add(event);
416
logEvent("ERROR", event);
417
return true; // Continue processing (recoverable error)
418
419
case ValidationEvent.FATAL_ERROR:
420
fatalErrors.add(event);
421
logEvent("FATAL", event);
422
return false; // Stop processing
423
424
default:
425
logEvent("UNKNOWN", event);
426
return true;
427
}
428
}
429
430
private void logEvent(String severity, ValidationEvent event) {
431
StringBuilder sb = new StringBuilder();
432
sb.append(String.format("[%s] %s", severity, event.getMessage()));
433
434
ValidationEventLocator locator = event.getLocator();
435
if (locator != null) {
436
if (locator.getLineNumber() != -1 || locator.getColumnNumber() != -1) {
437
sb.append(String.format(" (Line: %d, Column: %d)",
438
locator.getLineNumber(),
439
locator.getColumnNumber())
440
);
441
}
442
443
if (locator.getURL() != null) {
444
sb.append(String.format(" in %s", locator.getURL().toString()));
445
}
446
447
if (locator.getObject() != null) {
448
sb.append(String.format(" [Object: %s]",
449
locator.getObject().getClass().getSimpleName())
450
);
451
}
452
}
453
454
System.err.println(sb.toString());
455
456
// Log linked exception details
457
Throwable cause = event.getLinkedException();
458
if (cause != null) {
459
System.err.println(" Caused by: " + cause.getClass().getSimpleName() +
460
": " + cause.getMessage());
461
}
462
}
463
464
public ValidationSummary getSummary() {
465
return new ValidationSummary(warnings, errors, fatalErrors);
466
}
467
468
public void reset() {
469
warnings.clear();
470
errors.clear();
471
fatalErrors.clear();
472
}
473
474
// Summary class for reporting
475
public static class ValidationSummary {
476
private final List<ValidationEvent> warnings;
477
private final List<ValidationEvent> errors;
478
private final List<ValidationEvent> fatalErrors;
479
480
public ValidationSummary(List<ValidationEvent> warnings,
481
List<ValidationEvent> errors,
482
List<ValidationEvent> fatalErrors) {
483
this.warnings = new ArrayList<>(warnings);
484
this.errors = new ArrayList<>(errors);
485
this.fatalErrors = new ArrayList<>(fatalErrors);
486
}
487
488
public boolean hasIssues() {
489
return !warnings.isEmpty() || !errors.isEmpty() || !fatalErrors.isEmpty();
490
}
491
492
public boolean isValid() {
493
return errors.isEmpty() && fatalErrors.isEmpty();
494
}
495
496
public int getWarningCount() { return warnings.size(); }
497
public int getErrorCount() { return errors.size(); }
498
public int getFatalErrorCount() { return fatalErrors.size(); }
499
500
public List<ValidationEvent> getWarnings() { return new ArrayList<>(warnings); }
501
public List<ValidationEvent> getErrors() { return new ArrayList<>(errors); }
502
public List<ValidationEvent> getFatalErrors() { return new ArrayList<>(fatalErrors); }
503
504
@Override
505
public String toString() {
506
return String.format("ValidationSummary[Warnings: %d, Errors: %d, Fatal: %d]",
507
warnings.size(), errors.size(), fatalErrors.size());
508
}
509
}
510
}
511
512
// Usage example with detailed reporting
513
DetailedValidationEventHandler handler = new DetailedValidationEventHandler();
514
unmarshaller.setEventHandler(handler);
515
516
try {
517
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
518
519
ValidationSummary summary = handler.getSummary();
520
if (summary.hasIssues()) {
521
System.out.println(summary);
522
523
if (summary.isValid()) {
524
System.out.println("Document processed successfully despite warnings");
525
} else {
526
System.out.println("Document contains errors - data may be incomplete");
527
}
528
}
529
530
} catch (JAXBException e) {
531
System.err.println("Processing failed: " + e.getMessage());
532
}
533
```
534
535
### Integration with Logging Frameworks
536
537
Integration with popular Java logging frameworks for production-ready error handling.
538
539
```java
540
// SLF4J-based validation event handler
541
import org.slf4j.Logger;
542
import org.slf4j.LoggerFactory;
543
544
public class LoggingValidationEventHandler implements ValidationEventHandler {
545
private static final Logger logger = LoggerFactory.getLogger(LoggingValidationEventHandler.class);
546
547
@Override
548
public boolean handleEvent(ValidationEvent event) {
549
String message = formatEventMessage(event);
550
551
switch (event.getSeverity()) {
552
case ValidationEvent.WARNING:
553
logger.warn(message, event.getLinkedException());
554
return true;
555
556
case ValidationEvent.ERROR:
557
logger.error(message, event.getLinkedException());
558
return true; // Continue processing recoverable errors
559
560
case ValidationEvent.FATAL_ERROR:
561
logger.error("FATAL: " + message, event.getLinkedException());
562
return false; // Stop processing
563
564
default:
565
logger.info("UNKNOWN: " + message, event.getLinkedException());
566
return true;
567
}
568
}
569
570
private String formatEventMessage(ValidationEvent event) {
571
StringBuilder sb = new StringBuilder(event.getMessage());
572
573
ValidationEventLocator locator = event.getLocator();
574
if (locator != null) {
575
if (locator.getLineNumber() != -1) {
576
sb.append(" [Line: ").append(locator.getLineNumber()).append("]");
577
}
578
if (locator.getColumnNumber() != -1) {
579
sb.append(" [Column: ").append(locator.getColumnNumber()).append("]");
580
}
581
if (locator.getURL() != null) {
582
sb.append(" [URL: ").append(locator.getURL()).append("]");
583
}
584
}
585
586
return sb.toString();
587
}
588
}
589
```