CodeGym /Cursos /JAVA 25 SELF /Trabajo con XML mediante JAXB: fundamentos, anotaciones

Trabajo con XML mediante JAXB: fundamentos, anotaciones

JAVA 25 SELF
Nivel 47 , Lección 3
Disponible

1. Introducción a JAXB

JAXB (Java Architecture for XML Binding) — es una tecnología estándar de Java para transformar (binding) objetos Java en XML y viceversa. Con JAXB se pueden serializar fácilmente objetos a archivos XML y luego reconstruirlos a partir de esos archivos.

JAXB formaba parte de la biblioteca estándar de Java hasta la versión 11 inclusive. A partir de Java 11, JAXB se separó en un módulo independiente que hay que añadir mediante Maven/Gradle o descargar manualmente. Para versiones modernas de Java, añade las dependencias:

<!-- Ejemplo para Maven -->
<dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>4.0.3</version>
</dependency>

¿Para qué sirve XML?

  • XML es un formato universal y legible por humanos, ampliamente utilizado para el intercambio de datos entre sistemas, la configuración y el almacenamiento de información.
  • A diferencia de la serialización binaria, el XML se puede leer a simple vista, validar contra un esquema y abrir en el navegador.

2. Clases y anotaciones principales de JAXB

JAXB funciona sobre la base de anotaciones con las que se marcan las clases y sus campos para controlar el proceso de serialización/deserialización.

Anotaciones clave

Anotación Para qué sirve
@XmlRootElement
Indica el elemento raíz del XML (la clase en sí)
@XmlElement
Marca un campo/propiedad como elemento XML
@XmlAttribute
Marca un campo/propiedad como atributo XML
@XmlType
Controla el orden de los elementos, el nombre del tipo, etc.
@XmlTransient
Excluye el campo de la serialización

Clases principales

  • JAXBContext — punto de entrada; crea un contexto para la serialización/deserialización de clases concretas.
  • Marshaller — convierte un objeto a XML (marshalling, marshal()).
  • Unmarshaller — convierte XML a un objeto (unmarshalling, unmarshal()).

3. Ejemplo: serialización de un objeto a XML

Creemos una clase que vamos a serializar. Que sea un personaje para nuestro juego:

import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlAttribute;

@XmlRootElement(name = "player")
public class Player {
    private String name;
    private int level;
    private int health;

    public Player() {} // ¡Constructor vacío obligatorio!

    public Player(String name, int level, int health) {
        this.name = name;
        this.level = level;
        this.health = health;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) { this.name = name; }

    @XmlElement
    public int getLevel() {
        return level;
    }

    public void setLevel(int level) { this.level = level; }

    @XmlAttribute
    public int getHealth() {
        return health;
    }

    public void setHealth(int health) { this.health = health; }
}
  • @XmlRootElement(name = "player") — la clase se convierte en el elemento raíz <player>.
  • @XmlElement — el campo será un elemento XML independiente (<name>, <level>).
  • @XmlAttribute — el campo será un atributo del elemento raíz (health="100").
  • No olvides el constructor sin argumentos. JAXB lo requiere para la deserialización.

Serialización del objeto a XML

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;

public class Main {
    public static void main(String[] args) throws Exception {
        Player player = new Player("Aragorn", 5, 100);

        JAXBContext context = JAXBContext.newInstance(Player.class);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // Salida formateada

        marshaller.marshal(player, System.out); // Escribimos el XML en la consola
        // marshaller.marshal(player, new File("player.xml")); // O en un archivo
    }
}

Resultado:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<player health="100">
    <name>Aragorn</name>
    <level>5</level>
</player>

Deserialización de un objeto desde XML

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Unmarshaller;
import java.io.File;

public class Main {
    public static void main(String[] args) throws Exception {
        JAXBContext context = JAXBContext.newInstance(Player.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();

        Player player = (Player) unmarshaller.unmarshal(new File("player.xml"));
        System.out.println(player.getName() + ", nivel: " + player.getLevel() + ", salud: " + player.getHealth());
    }
}

4. Características y limitaciones de JAXB

Requisitos para las clases

  • Constructor público sin parámetros — obligatorio.
  • Para un funcionamiento correcto, utiliza getters y setters.
  • Todos los campos serializables deben ser accesibles (a través de la API pública).
  • Los objetos anidados y las colecciones también deben ser serializables (anótalos y añade un constructor vacío).

Trabajo con colecciones y objetos anidados

Supongamos que el jugador tiene un inventario (lista de objetos). ¿Cómo serializar una colección?

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;

@XmlRootElement(name = "player")
public class Player {
    // ... otros campos

    private List<String> inventory;

    public Player() {}

    // ... demás getters/setters

    @XmlElementWrapper(name = "inventory")
    @XmlElement(name = "item")
    public List<String> getInventory() {
        return inventory;
    }

    public void setInventory(List<String> inventory) {
        this.inventory = inventory;
    }
}

Resultado de la serialización:

<player health="100">
    <name>Aragorn</name>
    <level>5</level>
    <inventory>
        <item>Sword</item>
        <item>Shield</item>
    </inventory>
</player>
  • @XmlElementWrapper — crea una «envoltura» alrededor de la colección (elemento <inventory>).
  • @XmlElement(name = "item") — cada elemento de la lista se serializa como <item>.

Si hay objetos anidados (por ejemplo, Position), también deben anotarse y añadir un constructor vacío.

5. Práctica: serializar y deserializar un objeto en XML

import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementWrapper;
import jakarta.xml.bind.annotation.XmlAttribute;
import java.util.List;

@XmlRootElement(name = "player")
public class Player {
    private String name;
    private int level;
    private int health;
    private List<String> inventory;
    private Position position;

    public Player() {}

    public Player(String name, int level, int health, List<String> inventory, Position position) {
        this.name = name;
        this.level = level;
        this.health = health;
        this.inventory = inventory;
        this.position = position;
    }

    @XmlElement
    public String getName() { return name; }

    @XmlElement
    public int getLevel() { return level; }

    @XmlAttribute
    public int getHealth() { return health; }

    @XmlElementWrapper(name = "inventory")
    @XmlElement(name = "item")
    public List<String> getInventory() { return inventory; }

    @XmlElement
    public Position getPosition() { return position; }

    // setters omitidos por brevedad
}

@XmlRootElement(name = "position")
class Position {
    private int x;
    private int y;

    public Position() {}

    public Position(int x, int y) { this.x = x; this.y = y; }

    @XmlAttribute
    public int getX() { return x; }

    @XmlAttribute
    public int getY() { return y; }

    // setters omitidos
}

Serialización:

Player player = new Player(
    "Aragorn",
    5,
    100,
    List.of("Sword", "Shield", "Potion"),
    new Position(10, 20)
);

JAXBContext context = JAXBContext.newInstance(Player.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(player, System.out);

Resultado en XML:

<player health="100">
    <name>Aragorn</name>
    <level>5</level>
    <inventory>
        <item>Sword</item>
        <item>Shield</item>
        <item>Potion</item>
    </inventory>
    <position x="10" y="20"/>
</player>

La deserialización funciona de forma análoga: JAXB analizará los objetos anidados y las colecciones si las clases están descritas correctamente.

6. Tabla: anotaciones principales de JAXB y su efecto

Anotación Dónde usar Qué hace en XML
@XmlRootElement
Clase Elemento raíz
@XmlElement
Getter/campo Elemento dentro de XML
@XmlAttribute
Getter/campo Atributo del elemento
@XmlElementWrapper
Getter de colección «Envoltura» de la colección (por ejemplo, <list>)
@XmlTransient
Campo/getter Excluye el campo de la serialización
@XmlType
Clase Controla el orden de los elementos y el nombre del tipo

7. Características y limitaciones de JAXB

Orden de los elementos

De forma predeterminada, JAXB puede emitir los elementos en orden alfabético. Para especificar el orden explícitamente, utiliza @XmlType y la propiedad propOrder:

@XmlType(propOrder = {"name", "level", "inventory", "position"})

Exclusión de campos

Para no serializar un campo/getter, usa @XmlTransient:

@XmlTransient
public String getSecretCode() { ... }

Problemas con colecciones

  • No utilices colecciones «raw» sin genéricos: escribe List<Type>, no List.
  • Si la colección almacena objetos, sus clases también deben estar anotadas y tener un constructor vacío.

Errores

  • Falta el constructor vacío — obtendrás JAXBException al hacer unmarshalling.
  • Clase anidada no anotada — JAXB no podrá serializar/deserializarla.
  • Tipos no estándar (por ejemplo, LocalDate) requieren un adaptador (@XmlJavaTypeAdapter).

8. Errores comunes al trabajar con JAXB

Error n.º 1: Falta el constructor sin argumentos. JAXB exige que la clase serializable tenga un constructor público sin parámetros. Si no lo hay, durante el unmarshalling se lanzará la excepción JAXBException.

Error n.º 2: Objetos anidados sin anotar. Si tienes un campo-objeto pero su clase no está anotada con @XmlRootElement o al menos @XmlType, JAXB no podrá serializar/deserializarlo correctamente.

Error n.º 3: Problemas con colecciones. JAXB no entiende las colecciones «raw» sin indicar el tipo de sus elementos. Usa genéricos y anota correctamente las colecciones (@XmlElementWrapper + @XmlElement).

Error n.º 4: Gestión implícita del orden de los elementos. Si el orden de los elementos en el XML es importante para la integración, utiliza @XmlType con propOrder; de lo contrario, JAXB puede emitir los elementos en otro orden (por ejemplo, alfabético).

Error n.º 5: Uso de tipos no estándar sin adaptador. JAXB no sabe serializar algunos tipos (por ejemplo, LocalDate) sin un adaptador. Aplica @XmlJavaTypeAdapter o serializa el valor como cadena.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION