Java JAXB
Mapea XML a objetos Java y viceversa con anotaciones JAXB y Marshaller/Unmarshaller.
JAXB (Jakarta XML Binding, anteriormente Java Architecture for XML Binding) mapea objetos Java a XML y viceversa sin necesidad de escribir código de análisis a mano. Anotas una clase simple y la entregas a un Marshaller para producir XML o a un Unmarshaller para leer XML en objetos. JAXB vivía en el JDK (javax.xml.bind) hasta Java 8, fue eliminado en Java 11 y ahora se distribuye como una dependencia separada bajo el espacio de nombres jakarta.xml.bind. Las anotaciones y el modelo de marshal/unmarshal son los mismos en ambos casos.
Este capítulo cubre qué es el enlace JAXB, las anotaciones principales, cómo convertir un objeto a XML y leer XML de vuelta, cómo se mapean las colecciones y el cambio de espacio de nombres entre Java 8 y las versiones modernas de Java. JAXB es una API de enlace: a diferencia de los parsers de bajo nivel DOM y SAX cubiertos antes en esta parte, nunca tocas el árbol XML — trabajas con objetos Java ordinarios.
Cuándo usar JAXB
Usa JAXB cuando tus datos ya tienen (o merecen) una clase y XML es simplemente el formato de transporte o almacenamiento:
- Leer y escribir archivos de configuración o documentos donde la estructura es estable y conocida de antemano.
- Servicios web SOAP / heredados, donde el contrato es un esquema XML y las herramientas generan las clases.
- Ida y vuelta — cargar XML, mutar el objeto y volver a escribirlo sin análisis manual.
Usa DOM o SAX cuando la estructura es irregular, solo necesitas unos pocos campos de un documento grande o no existe una clase natural a la que enlazar. Y si controlas ambos extremos y solo necesitas un formato de datos compacto, JSON con Jackson suele ser más ligero que XML.
La idea central: las anotaciones describen el mapeo
No escribes código que recorra el árbol XML. En su lugar, describes con anotaciones en una clase cómo sus campos corresponden a elementos y atributos XML. JAXB lee esas anotaciones en tiempo de ejecución y genera la conversión por ti en ambas direcciones. Un POJO se convierte en un esquema autodocumentado.
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() {}
}Las anotaciones principales
Un puñado de anotaciones cubre casi todos los mapeos. Viven en el paquete jakarta.xml.bind.annotation (o javax.xml.bind.annotation en Java 8).
| Anotación | Efecto |
|---|---|
@XmlRootElement | Marca una clase como raíz del documento; nombra el elemento más externo |
@XmlElement | Mapea un campo/propiedad a un elemento anidado |
@XmlAttribute | Mapea un campo/propiedad a un atributo en su elemento |
@XmlElementWrapper | Envuelve una colección en un elemento contenedor |
@XmlTransient | Excluye un campo del XML por completo |
@XmlAccessorType | Controla si JAXB enlaza campos o getters por defecto |
Marshalling: objeto a XML
Un JAXBContext es el punto de entrada — créalo para tus clases raíz y luego pídele un Marshaller. El marshaller convierte un grafo de objetos en XML. Configurar JAXB_FORMATTED_OUTPUT produce una salida legible e indentada.
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 a objeto
La operación inversa es simétrica: pídele al mismo JAXBContext un Unmarshaller y apúntalo a una fuente — un File, InputStream, Reader o StringReader. JAXB construye el objeto usando el constructor sin argumentos y lo rellena con los elementos y atributos.
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 no está en el classpath de este ejecutor de código (es una dependencia externa en Java moderno), por lo que el ejemplo práctico a continuación demuestra el mismo ciclo de marshal/unmarshal de ida y vuelta usando únicamente la API DOM integrada del JDK. El concepto es idéntico: un atributo en la raíz, elementos hijos para los campos y un viaje de regreso a un objeto igual.
Qué extraer de la ejecución:
- El XML marshalled coloca
yearcomo un atributo en<book>perotitleyauthorcomo elementos hijos — exactamente la división que controlan@XmlAttributeversus@XmlElementen JAXB real. La elección de anotación es la que decide elemento-vs-atributo. - La etiqueta raíz es
book, reportada porel.getTagName(). En JAXB ese nombre proviene de@XmlRootElement(name = "book"); aquí es la cadena pasada acreateElement. De cualquier manera, el elemento más externo identifica el tipo del documento. - Marshalling y unmarshalling son operaciones espejo sobre la misma estructura: el programa construye XML a partir de un
Book, luego reconstruye unBookdesde ese XML. ElMarshallery elUnmarshallerde JAXB son exactamente este par, respaldados por un únicoJAXBContext. round-trip equal : trueprueba que los datos sobrevivieron el viaje intactos — título, autor y año regresaron todos. Un enlace correcto no tiene pérdidas, que es la propiedad en la que confías cuando XML es tu formato de transporte.- Leer
yearde vuelta requirióInteger.parseIntporque XML es todo texto. JAXB oculta esto convirtiendo el texto de atributos y elementos al tipo Java declarado (int,LocalDate,BigDecimal) automáticamente; sin él, cada campo es una cadena que debes analizar tú mismo.
Mapeo de colecciones
Un List se mapea a elementos repetidos. Por defecto cada elemento recibe el nombre del campo, lo que puede producir un documento plano y difícil de leer. @XmlElementWrapper agrega un elemento contenedor para que los elementos queden agrupados — el patrón común y legible.
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() {}
}Con el wrapper, la salida se anida limpiamente:
<library>
<books>
<book year="2018"><title>Effective Java</title>...</book>
<book year="2008"><title>Clean Code</title>...</book>
</books>
</library>Elimina @XmlElementWrapper y los elementos <book> quedan directamente bajo <library> sin ningún elemento agrupador — válido, pero más plano. Elegir entre los dos es la decisión de mapeo de colecciones más común en JAXB.
Java 8 vs. Java moderno: el cambio de espacio de nombres
El mayor problema es el cambio de nombre del paquete. En Java 8 la API está incluida y vive bajo javax.xml.bind. A partir de Java 11 está desvinculada y vive bajo jakarta.xml.bind, incorporada como dependencia.
| Java 8 | Java 11+ | |
|---|---|---|
| Paquete | javax.xml.bind | jakarta.xml.bind |
| ¿En el classpath? | Integrado | Agregar una dependencia |
| Artefacto de tiempo de ejecución | JDK | org.glassfish.jaxb:jaxb-runtime |
Para una compilación Maven en Java moderno, agregas la API más una implementación de tiempo de ejecución:
<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>Práctica
Ver también
- Trabajar con XML en Java — descripción general de las APIs XML y cuándo encaja cada una.
- Parser DOM y parser SAX — las alternativas de bajo nivel al enlace.
- Jackson — la misma idea de anotar y enlazar aplicada a JSON.