JAXB XmlAdapter for Non-Standard Data Types

The Java Architecture for XML Binding (JAXB) framework uses the XmlAdapter class to map non-standard, unannotated, or complex Java data types to easily serializable XML formats. By defining a two-way translation layer between an unmappable Java type and a JAXB-friendly representation, XmlAdapter allows developers to serialize objects such as modern date-time classes, interfaces, third-party library classes, and custom map structures without altering the underlying domain models.

The Role of XmlAdapter

By default, JAXB can only marshal and unmarshal types that adhere to the JavaBean convention, primitive types, standard collections, or classes decorated with JAXB annotations. When encountering unsupported types—such as java.time.LocalDate, abstract types, or third-party objects that cannot be directly annotated—JAXB throws runtime exceptions during serialization.

The abstract class jakarta.xml.bind.annotation.adapters.XmlAdapter<ValueType, BoundType> resolves this by acting as an intermediary. It uses two generic parameters:

Key Methods

To create a custom adapter, you extend XmlAdapter and implement two essential methods:

  1. marshal(BoundType v): Invoked during serialization (Java to XML). It converts the complex BoundType into the simpler ValueType.
  2. unmarshal(ValueType v): Invoked during deserialization (XML to Java). It converts the simple ValueType back into the target BoundType.

Implementation Example

To serialize a java.time.LocalDate object into an ISO-8601 string format, create a custom adapter:

import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {

    @Override
    public String marshal(LocalDate date) throws Exception {
        return date != null ? date.format(DateTimeFormatter.ISO_LOCAL_DATE) : null;
    }

    @Override
    public LocalDate unmarshal(String dateString) throws Exception {
        return dateString != null ? LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE) : null;
    }
}

Applying the Adapter

Once implemented, the adapter must be registered using the @XmlJavaTypeAdapter annotation. JAXB supports application at multiple levels:

Handling Complex Structures

For structures like java.util.Map, which JAXB cannot handle out of the box, ValueType can be a custom JAXB-annotated class containing a list of key-value entries. The adapter then converts the Map into a list of entry objects during marshalling and reconstructs the Map during unmarshalling, ensuring clean and compliant XML generation.