Jakarta XML Binding API that automates the mapping between XML documents and Java objects through data binding
—
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.
The core validation event system provides structured error reporting with severity levels and location information.
public interface ValidationEvent {
// Severity constants
int WARNING = 0;
int ERROR = 1;
int FATAL_ERROR = 2;
// Event information
int getSeverity();
String getMessage();
Throwable getLinkedException();
ValidationEventLocator getLocator();
}
public interface ValidationEventHandler {
boolean handleEvent(ValidationEvent event);
}
public interface ValidationEventLocator {
java.net.URL getURL();
int getOffset();
int getLineNumber();
int getColumnNumber();
Object getObject();
org.w3c.dom.Node getNode();
}Usage Examples:
// Custom validation event handler
public class CustomValidationEventHandler implements ValidationEventHandler {
@Override
public boolean handleEvent(ValidationEvent event) {
String severity = getSeverityString(event.getSeverity());
String message = event.getMessage();
ValidationEventLocator locator = event.getLocator();
System.err.printf("[%s] %s%n", severity, message);
if (locator != null) {
System.err.printf(" Location: Line %d, Column %d%n",
locator.getLineNumber(),
locator.getColumnNumber()
);
if (locator.getURL() != null) {
System.err.printf(" URL: %s%n", locator.getURL());
}
}
// Handle linked exception
Throwable cause = event.getLinkedException();
if (cause != null) {
System.err.printf(" Caused by: %s%n", cause.getMessage());
}
// Continue processing for warnings and errors, stop for fatal errors
return event.getSeverity() != ValidationEvent.FATAL_ERROR;
}
private String getSeverityString(int severity) {
switch (severity) {
case ValidationEvent.WARNING: return "WARNING";
case ValidationEvent.ERROR: return "ERROR";
case ValidationEvent.FATAL_ERROR: return "FATAL";
default: return "UNKNOWN";
}
}
}
// Apply to marshaller/unmarshaller
JAXBContext context = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setEventHandler(new CustomValidationEventHandler());
// Validation events will be sent to custom handler
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));Jakarta XML Binding defines specialized validation event types for specific error scenarios.
public interface ParseConversionEvent extends ValidationEvent {
// Inherits all ValidationEvent methods
// Represents errors during parsing/conversion from string to Java types
}
public interface PrintConversionEvent extends ValidationEvent {
// Inherits all ValidationEvent methods
// Represents errors during formatting/conversion from Java types to string
}
public interface NotIdentifiableEvent extends ValidationEvent {
// Inherits all ValidationEvent methods
// Represents errors when objects cannot be identified for marshalling
}Event Type Usage:
public class TypedValidationEventHandler implements ValidationEventHandler {
@Override
public boolean handleEvent(ValidationEvent event) {
if (event instanceof ParseConversionEvent) {
handleParseError((ParseConversionEvent) event);
} else if (event instanceof PrintConversionEvent) {
handlePrintError((PrintConversionEvent) event);
} else if (event instanceof NotIdentifiableEvent) {
handleIdentificationError((NotIdentifiableEvent) event);
} else {
handleGenericError(event);
}
// Continue processing unless fatal
return event.getSeverity() != ValidationEvent.FATAL_ERROR;
}
private void handleParseError(ParseConversionEvent event) {
System.err.println("Parse conversion error: " + event.getMessage());
// Log parsing-specific information
}
private void handlePrintError(PrintConversionEvent event) {
System.err.println("Print conversion error: " + event.getMessage());
// Log formatting-specific information
}
private void handleIdentificationError(NotIdentifiableEvent event) {
System.err.println("Object identification error: " + event.getMessage());
// Handle unidentifiable object scenarios
}
private void handleGenericError(ValidationEvent event) {
System.err.println("Validation error: " + event.getMessage());
}
}Jakarta XML Binding provides default implementations for common validation scenarios.
// In jakarta.xml.bind.helpers package
public class DefaultValidationEventHandler implements ValidationEventHandler {
public boolean handleEvent(ValidationEvent event);
}
public class ValidationEventCollector implements ValidationEventHandler {
public ValidationEvent[] getEvents();
public boolean hasEvents();
public void reset();
public boolean handleEvent(ValidationEvent event);
}Usage Examples:
// Default handler (terminates on first error)
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setEventHandler(new DefaultValidationEventHandler());
// Event collector (collects all events for later review)
ValidationEventCollector eventCollector = new ValidationEventCollector();
unmarshaller.setEventHandler(eventCollector);
try {
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
// Check collected events after processing
if (eventCollector.hasEvents()) {
ValidationEvent[] events = eventCollector.getEvents();
System.out.printf("Processing completed with %d validation events:%n", events.length);
for (ValidationEvent event : events) {
System.err.printf(" [%s] %s%n",
getSeverityString(event.getSeverity()),
event.getMessage()
);
}
}
} catch (JAXBException e) {
System.err.println("Fatal error during unmarshalling: " + e.getMessage());
}
// Reset collector for reuse
eventCollector.reset();Jakarta XML Binding integrates with javax.xml.validation for XML Schema validation during binding operations.
// In Marshaller and Unmarshaller interfaces
public interface Marshaller {
void setSchema(javax.xml.validation.Schema schema);
javax.xml.validation.Schema getSchema();
}
public interface Unmarshaller {
void setSchema(javax.xml.validation.Schema schema);
javax.xml.validation.Schema getSchema();
}Schema Validation Examples:
// Load XML Schema
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new File("person.xsd"));
// Set up validation event handler
ValidationEventCollector eventCollector = new ValidationEventCollector();
// Configure unmarshaller with schema validation
JAXBContext context = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setSchema(schema);
unmarshaller.setEventHandler(eventCollector);
try {
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
// Check for schema validation errors
if (eventCollector.hasEvents()) {
for (ValidationEvent event : eventCollector.getEvents()) {
System.err.printf("Schema validation error: %s%n", event.getMessage());
}
} else {
System.out.println("Document is valid according to schema");
}
} catch (JAXBException e) {
System.err.println("Unmarshalling failed: " + e.getMessage());
}
// Configure marshaller with schema validation
Marshaller marshaller = context.createMarshaller();
marshaller.setSchema(schema);
marshaller.setEventHandler(eventCollector);
eventCollector.reset();
try {
marshaller.marshal(person, System.out);
if (eventCollector.hasEvents()) {
System.err.println("Generated XML does not conform to schema");
}
} catch (JAXBException e) {
System.err.println("Marshalling failed: " + e.getMessage());
}Jakarta XML Binding defines a comprehensive exception hierarchy for different error scenarios.
public class JAXBException extends Exception {
public JAXBException(String message);
public JAXBException(String message, String errorCode);
public JAXBException(Throwable exception);
public JAXBException(String message, Throwable exception);
public JAXBException(String message, String errorCode, Throwable exception);
public String getErrorCode();
public Throwable getLinkedException();
public void setLinkedException(Throwable exception);
}
public class MarshalException extends JAXBException {
// Same constructors as JAXBException
// Thrown during marshalling operations
}
public class UnmarshalException extends JAXBException {
// Same constructors as JAXBException
// Thrown during unmarshalling operations
}
public class ValidationException extends JAXBException {
// Same constructors as JAXBException
// Thrown during validation operations
}
public class PropertyException extends JAXBException {
// Same constructors as JAXBException
// Thrown for property-related errors
}
public class DataBindingException extends RuntimeException {
public DataBindingException(String message, Throwable cause);
public DataBindingException(Throwable cause);
// Runtime exception used by convenience JAXB class
}
public class TypeConstraintException extends RuntimeException {
public TypeConstraintException(String message);
public TypeConstraintException(String message, Throwable cause);
public TypeConstraintException(Throwable cause);
// Runtime exception for type constraint violations
}Exception Handling Examples:
// Comprehensive exception handling
public class RobustXMLProcessor {
public Person loadPerson(File xmlFile) {
try {
JAXBContext context = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
return (Person) unmarshaller.unmarshal(xmlFile);
} catch (UnmarshalException e) {
System.err.println("Failed to unmarshal XML: " + e.getMessage());
// Check for linked exceptions
Throwable cause = e.getLinkedException();
if (cause instanceof SAXParseException) {
SAXParseException saxError = (SAXParseException) cause;
System.err.printf("XML parsing error at line %d, column %d: %s%n",
saxError.getLineNumber(),
saxError.getColumnNumber(),
saxError.getMessage()
);
}
return null;
} catch (ValidationException e) {
System.err.println("Validation error: " + e.getMessage());
return null;
} catch (JAXBException e) {
System.err.println("JAXB error: " + e.getMessage());
String errorCode = e.getErrorCode();
if (errorCode != null) {
System.err.println("Error code: " + errorCode);
}
return null;
}
}
public boolean savePerson(Person person, File xmlFile) {
try {
JAXBContext context = JAXBContext.newInstance(Person.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(person, xmlFile);
return true;
} catch (MarshalException e) {
System.err.println("Failed to marshal object: " + e.getMessage());
return false;
} catch (PropertyException e) {
System.err.println("Property configuration error: " + e.getMessage());
return false;
} catch (JAXBException e) {
System.err.println("JAXB error: " + e.getMessage());
return false;
}
}
}Advanced error handling with detailed reporting and recovery strategies.
// Advanced validation event handler with detailed reporting
public class DetailedValidationEventHandler implements ValidationEventHandler {
private final List<ValidationEvent> warnings = new ArrayList<>();
private final List<ValidationEvent> errors = new ArrayList<>();
private final List<ValidationEvent> fatalErrors = new ArrayList<>();
@Override
public boolean handleEvent(ValidationEvent event) {
// Categorize events by severity
switch (event.getSeverity()) {
case ValidationEvent.WARNING:
warnings.add(event);
logEvent("WARNING", event);
return true; // Continue processing
case ValidationEvent.ERROR:
errors.add(event);
logEvent("ERROR", event);
return true; // Continue processing (recoverable error)
case ValidationEvent.FATAL_ERROR:
fatalErrors.add(event);
logEvent("FATAL", event);
return false; // Stop processing
default:
logEvent("UNKNOWN", event);
return true;
}
}
private void logEvent(String severity, ValidationEvent event) {
StringBuilder sb = new StringBuilder();
sb.append(String.format("[%s] %s", severity, event.getMessage()));
ValidationEventLocator locator = event.getLocator();
if (locator != null) {
if (locator.getLineNumber() != -1 || locator.getColumnNumber() != -1) {
sb.append(String.format(" (Line: %d, Column: %d)",
locator.getLineNumber(),
locator.getColumnNumber())
);
}
if (locator.getURL() != null) {
sb.append(String.format(" in %s", locator.getURL().toString()));
}
if (locator.getObject() != null) {
sb.append(String.format(" [Object: %s]",
locator.getObject().getClass().getSimpleName())
);
}
}
System.err.println(sb.toString());
// Log linked exception details
Throwable cause = event.getLinkedException();
if (cause != null) {
System.err.println(" Caused by: " + cause.getClass().getSimpleName() +
": " + cause.getMessage());
}
}
public ValidationSummary getSummary() {
return new ValidationSummary(warnings, errors, fatalErrors);
}
public void reset() {
warnings.clear();
errors.clear();
fatalErrors.clear();
}
// Summary class for reporting
public static class ValidationSummary {
private final List<ValidationEvent> warnings;
private final List<ValidationEvent> errors;
private final List<ValidationEvent> fatalErrors;
public ValidationSummary(List<ValidationEvent> warnings,
List<ValidationEvent> errors,
List<ValidationEvent> fatalErrors) {
this.warnings = new ArrayList<>(warnings);
this.errors = new ArrayList<>(errors);
this.fatalErrors = new ArrayList<>(fatalErrors);
}
public boolean hasIssues() {
return !warnings.isEmpty() || !errors.isEmpty() || !fatalErrors.isEmpty();
}
public boolean isValid() {
return errors.isEmpty() && fatalErrors.isEmpty();
}
public int getWarningCount() { return warnings.size(); }
public int getErrorCount() { return errors.size(); }
public int getFatalErrorCount() { return fatalErrors.size(); }
public List<ValidationEvent> getWarnings() { return new ArrayList<>(warnings); }
public List<ValidationEvent> getErrors() { return new ArrayList<>(errors); }
public List<ValidationEvent> getFatalErrors() { return new ArrayList<>(fatalErrors); }
@Override
public String toString() {
return String.format("ValidationSummary[Warnings: %d, Errors: %d, Fatal: %d]",
warnings.size(), errors.size(), fatalErrors.size());
}
}
}
// Usage example with detailed reporting
DetailedValidationEventHandler handler = new DetailedValidationEventHandler();
unmarshaller.setEventHandler(handler);
try {
Person person = (Person) unmarshaller.unmarshal(new File("person.xml"));
ValidationSummary summary = handler.getSummary();
if (summary.hasIssues()) {
System.out.println(summary);
if (summary.isValid()) {
System.out.println("Document processed successfully despite warnings");
} else {
System.out.println("Document contains errors - data may be incomplete");
}
}
} catch (JAXBException e) {
System.err.println("Processing failed: " + e.getMessage());
}Integration with popular Java logging frameworks for production-ready error handling.
// SLF4J-based validation event handler
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingValidationEventHandler implements ValidationEventHandler {
private static final Logger logger = LoggerFactory.getLogger(LoggingValidationEventHandler.class);
@Override
public boolean handleEvent(ValidationEvent event) {
String message = formatEventMessage(event);
switch (event.getSeverity()) {
case ValidationEvent.WARNING:
logger.warn(message, event.getLinkedException());
return true;
case ValidationEvent.ERROR:
logger.error(message, event.getLinkedException());
return true; // Continue processing recoverable errors
case ValidationEvent.FATAL_ERROR:
logger.error("FATAL: " + message, event.getLinkedException());
return false; // Stop processing
default:
logger.info("UNKNOWN: " + message, event.getLinkedException());
return true;
}
}
private String formatEventMessage(ValidationEvent event) {
StringBuilder sb = new StringBuilder(event.getMessage());
ValidationEventLocator locator = event.getLocator();
if (locator != null) {
if (locator.getLineNumber() != -1) {
sb.append(" [Line: ").append(locator.getLineNumber()).append("]");
}
if (locator.getColumnNumber() != -1) {
sb.append(" [Column: ").append(locator.getColumnNumber()).append("]");
}
if (locator.getURL() != null) {
sb.append(" [URL: ").append(locator.getURL()).append("]");
}
}
return sb.toString();
}
}Install with Tessl CLI
npx tessl i tessl/maven-jakarta-xml-bind--jakarta-xml-bind-api