Java JAXB
Map XML to Java objects and back in Java with JAXB annotations and Marshaller/Unmarshaller.
JAXB (Jakarta XML Binding, formerly Java Architecture for XML Binding) maps Java objects to XML and back without you writing parsing code by hand. You annotate a plain class, then hand it to a Marshaller to produce XML or an Unmarshaller to read XML into objects. JAXB lived in the JDK (javax.xml.bind) through Java 8, was removed in Java 11, and now ships as a separate dependency under the jakarta.xml.bind namespace. The annotations and the marshal/unmarshal model are the same in both.
This chapter covers what JAXB binding is, the core annotations, how to marshal an object to XML and unmarshal XML back, how collections are mapped, and the Java 8 vs. modern-Java namespace change. JAXB is a binding API: unlike the lower-level DOM and SAX parsers covered earlier in this part, you never touch the XML tree — you work with ordinary Java objects.
When to reach for JAXB
Use JAXB when your data already has (or deserves) a class, and XML is just the wire or storage format:
- Reading and writing config or document files where the shape is stable and known ahead of time.
- SOAP / legacy web services, where the contract is an XML schema and tools generate the classes.
- Round-tripping — loading XML, mutating the object, and writing it back out with no manual parsing.
Reach for DOM or SAX instead when the structure is irregular, you only need a few fields out of a large document, or there is no natural class to bind to. And if you control both ends and just need a compact data format, JSON with Jackson is usually lighter than XML.
The big idea: annotations describe the mapping
You do not write code that walks the XML tree. Instead you describe, with annotations on a class, how its fields correspond to XML elements and attributes. JAXB reads those annotations at runtime and generates the conversion for you in both directions. A POJO becomes a self-documenting schema.
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlAttribute;
@XmlRootElement(name = "book")
public class Book {
private String title;
private String author;
private int year;
@XmlElement public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
@XmlElement public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
@XmlAttribute public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
// JAXB requires a public no-arg constructor for unmarshalling
public Book() {}
}The core annotations
A handful of annotations cover almost every mapping. They live in the jakarta.xml.bind.annotation package (or javax.xml.bind.annotation on Java 8).
| Annotation | Effect |
|---|---|
@XmlRootElement | Marks a class as a document root; names the outermost element |
@XmlElement | Maps a field/property to a nested element |
@XmlAttribute | Maps a field/property to an attribute on its element |
@XmlElementWrapper | Wraps a collection in a containing element |
@XmlTransient | Excludes a field from the XML entirely |
@XmlAccessorType | Controls whether JAXB binds fields or getters by default |
Marshalling: object to XML
A JAXBContext is the entry point — create one for your root classes, then ask it for a Marshaller. The marshaller turns an object graph into XML. Setting JAXB_FORMATTED_OUTPUT gives human-readable, indented output.
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;
Book book = new Book();
book.setTitle("Effective Java");
book.setAuthor("Joshua Bloch");
book.setYear(2018);
JAXBContext context = JAXBContext.newInstance(Book.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(book, System.out);
// <book year="2018"><title>Effective Java</title><author>Joshua Bloch</author></book>Unmarshalling: XML to object
The reverse is symmetric: ask the same JAXBContext for an Unmarshaller and point it at a source — a File, InputStream, Reader, or StringReader. JAXB constructs the object using the no-arg constructor and populates it from the elements and attributes.
import jakarta.xml.bind.Unmarshaller;
import java.io.StringReader;
String xml = "<book year=\"2018\">"
+ "<title>Effective Java</title>"
+ "<author>Joshua Bloch</author></book>";
Unmarshaller unmarshaller = context.createUnmarshaller();
Book book = (Book) unmarshaller.unmarshal(new StringReader(xml));
System.out.println(book.getTitle()); // Effective Java
System.out.println(book.getYear()); // 2018JAXB is not on this code runner's classpath (it is an external dependency on modern Java), so the worked example below demonstrates the same marshal/unmarshal round trip using only the JDK's built-in DOM API. The concept is identical: an attribute on the root, child elements for the fields, and a round trip back to an equal object.
What to take from the run:
- The marshalled XML puts
yearas an attribute on<book>buttitleandauthoras child elements — exactly the split that@XmlAttributeversus@XmlElementcontrols in real JAXB. The annotation choice is what decides element-vs-attribute. - The root tag is
book, reported byel.getTagName(). In JAXB that name comes from@XmlRootElement(name = "book"); here it is the string passed tocreateElement. Either way, the outermost element identifies the document's type. - Marshalling and unmarshalling are mirror operations on the same shape: the program builds XML from a
Book, then reconstructs aBookfrom that XML. JAXB'sMarshallerandUnmarshallerare this exact pair backed by oneJAXBContext. round-trip equal : trueproves the data survived the trip intact — title, author, and year all came back. A correct binding is loss-free, which is the property you rely on when XML is your wire format.- Reading
yearback requiredInteger.parseIntbecause XML is all text. JAXB hides this by converting attribute and element text to the declared Java type (int,LocalDate,BigDecimal) automatically; without it, every field is a string you must parse yourself.
Mapping collections
A List maps to repeated elements. By default each item is named after the field, which can produce a flat, hard-to-read document. @XmlElementWrapper adds a containing element so the items are grouped — the common, readable pattern.
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
@XmlRootElement(name = "library")
public class Library {
private List<Book> books;
@XmlElementWrapper(name = "books") // outer <books> element
@XmlElement(name = "book") // each item is a <book>
public List<Book> getBooks() { return books; }
public void setBooks(List<Book> books) { this.books = books; }
public Library() {}
}With the wrapper, the output nests cleanly:
<library>
<books>
<book year="2018"><title>Effective Java</title>...</book>
<book year="2008"><title>Clean Code</title>...</book>
</books>
</library>Drop @XmlElementWrapper and the <book> elements sit directly under <library> with no grouping element — valid, but flatter. Choosing between the two is the most common collection-mapping decision in JAXB.
Java 8 vs. modern Java: the namespace move
The single biggest gotcha is the package rename. On Java 8 the API is bundled and lives under javax.xml.bind. From Java 11 onward it is unbundled and lives under jakarta.xml.bind, pulled in as a dependency.
| Java 8 | Java 11+ | |
|---|---|---|
| Package | javax.xml.bind | jakarta.xml.bind |
| On the classpath? | Built in | Add a dependency |
| Runtime artifact | JDK | org.glassfish.jaxb:jaxb-runtime |
For a Maven build on modern Java, you add the API plus a runtime implementation:
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
</dependency>Practice
See also
- Working with XML in Java — overview of the XML APIs and when each fits.
- DOM parser and SAX parser — the lower-level alternatives to binding.
- Jackson — the same annotate-and-bind idea applied to JSON.