CodeGym /Corsi /JAVA 25 SELF /Lavorare con XML tramite JAXB: basi, annotazioni

Lavorare con XML tramite JAXB: basi, annotazioni

JAVA 25 SELF
Livello 47 , Lezione 3
Disponibile

1. Introduzione a JAXB

JAXB (Java Architecture for XML Binding) è una tecnologia standard di Java per convertire (binding) oggetti Java in XML e viceversa. Con JAXB è facile serializzare gli oggetti in file XML e poi ricrearli da tali file.

JAXB faceva parte della libreria standard di Java fino alla versione 11 inclusa. A partire da Java 11, JAXB è stato spostato in un modulo separato che va aggiunto tramite Maven/Gradle o scaricato manualmente. Per le versioni moderne di Java aggiungete le dipendenze:

<!-- Esempio per 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>

Perché mai serve XML?

  • XML è un formato universale e leggibile dall’uomo, ampiamente usato per lo scambio di dati tra sistemi, per la configurazione e l’archiviazione delle informazioni.
  • A differenza della serializzazione binaria, l’XML è facile da leggere, da validare tramite schema e da aprire in un browser.

2. Classi e annotazioni principali di JAXB

JAXB funziona sulla base di annotazioni con cui si marcano le classi e i loro campi per controllare il processo di serializzazione/deserializzazione.

Annotazioni principali

Annotazione A cosa serve
@XmlRootElement
Indica l’elemento radice XML (la classe stessa)
@XmlElement
Contrassegna un campo/proprietà come elemento XML
@XmlAttribute
Contrassegna un campo/proprietà come attributo XML
@XmlType
Controlla l’ordine degli elementi, il nome del tipo, ecc.
@XmlTransient
Esclude il campo dalla serializzazione

Classi principali

  • JAXBContext — punto d’ingresso, crea il contesto per la serializzazione/deserializzazione di classi specifiche.
  • Marshaller — trasforma un oggetto in XML (marshalling, marshal()).
  • Unmarshaller — trasforma XML in un oggetto (unmarshalling, unmarshal()).

3. Esempio: serializzazione di un oggetto in XML

Creiamo una classe da serializzare. Sia un personaggio per il nostro gioco:

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() {} // Costruttore vuoto obbligatorio!

    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 classe diventa l’elemento radice <player>.
  • @XmlElement — il campo sarà un elemento XML separato (<name>, <level>).
  • @XmlAttribute — il campo sarà un attributo dell’elemento radice (health="100").
  • Non dimenticate il costruttore vuoto! JAXB lo richiede per la deserializzazione.

Serializzare un oggetto in 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); // Output leggibile

        marshaller.marshal(player, System.out); // Scriviamo l'XML sulla console
        // marshaller.marshal(player, new File("player.xml")); // Oppure su file
    }
}

Risultato:

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

Deserializzazione di un oggetto da 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() + ", livello: " + player.getLevel() + ", salute: " + player.getHealth());
    }
}

4. Caratteristiche e limitazioni di JAXB

Requisiti per le classi

  • Costruttore pubblico senza parametri — obbligatorio.
  • Per un funzionamento corretto usate getter e setter.
  • Tutti i campi serializzabili devono essere accessibili (tramite API pubblica).
  • Anche gli oggetti annidati e le collezioni devono essere serializzabili (annotateli e aggiungete un costruttore vuoto).

Lavorare con collezioni e oggetti annidati

Supponiamo che il giocatore abbia un inventario (lista di oggetti). Come serializzare una collezione?

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

@XmlRootElement(name = "player")
public class Player {
    // ... altri campi

    private List<String> inventory;

    public Player() {}

    // ... altri getter/setter

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

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

Risultato della serializzazione:

<player health="100">
    <name>Aragorn</name>
    <level>5</level>
    <inventory>
        <item>Sword</item>
        <item>Shield</item>
    </inventory>
</player>
  • @XmlElementWrapper — crea un «involucro» attorno alla collezione (l’elemento <inventory>).
  • @XmlElement(name = "item") — ogni elemento della lista viene serializzato come <item>.

Se avete oggetti annidati (ad esempio, Position), vanno anch’essi annotati e dotati di un costruttore vuoto.

5. Pratica: serializzare e deserializzare un oggetto in 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; }

    // setter omessi per brevità
}

@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; }

    // setter omessi
}

Serializzazione:

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);

Risultato 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 deserializzazione funziona in modo analogo: JAXB gestirà automaticamente oggetti annidati e collezioni se le classi sono descritte correttamente.

6. Tabella: annotazioni principali di JAXB e loro effetto

Annotazione Dove usarla Cosa produce in XML
@XmlRootElement
Classe Elemento radice
@XmlElement
Getter/campo Elemento interno XML
@XmlAttribute
Getter/campo Attributo dell’elemento
@XmlElementWrapper
Getter della collezione «Involucro» della collezione (per esempio, <list>)
@XmlTransient
Campo/getter Esclude il campo dalla serializzazione
@XmlType
Classe Controlla l’ordine degli elementi, il nome del tipo

7. Caratteristiche e limitazioni di JAXB

Ordine degli elementi

Per impostazione predefinita JAXB può emettere gli elementi in ordine alfabetico. Per specificare esplicitamente l’ordine, usate @XmlType e la proprietà propOrder:

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

Esclusione di campi

Per non serializzare un campo/getter, usate @XmlTransient:

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

Problemi con le collezioni

  • Non usate collezioni «raw» senza generics: scrivete List<Type>, non List.
  • Se la collezione contiene oggetti, anche le loro classi devono essere annotate e avere un costruttore vuoto.

Errori

  • Manca il costruttore vuoto — si otterrà una JAXBException durante l’unmarshalling.
  • Classe annidata non annotata — JAXB non riuscirà a serializzarla/deserializzarla.
  • Tipi non standard (ad esempio, LocalDate) richiedono un adapter (@XmlJavaTypeAdapter).

8. Errori tipici nell’uso di JAXB

Errore n. 1: assenza del costruttore vuoto. JAXB richiede che la classe serializzabile abbia un costruttore pubblico senza parametri. In caso contrario — durante l’unmarshalling si verificherà l’eccezione JAXBException.

Errore n. 2: oggetti annidati non annotati. Se avete un campo‑oggetto, ma la sua classe non è annotata con @XmlRootElement o almeno con @XmlType, JAXB non riuscirà a serializzarlo/deserializzarlo correttamente.

Errore n. 3: problemi con le collezioni. JAXB non gestisce collezioni «raw» senza indicazione del tipo degli elementi. Usate i generics e annotate correttamente le collezioni (@XmlElementWrapper + @XmlElement).

Errore n. 4: gestione implicita dell’ordine degli elementi. Se l’ordine degli elementi in XML è importante per l’integrazione, usate @XmlType con propOrder; altrimenti JAXB può emettere gli elementi in un ordine diverso (ad es. alfabetico).

Errore n. 5: uso di tipi non standard senza adapter. JAXB non sa serializzare alcuni tipi (ad esempio, LocalDate) senza un adapter. Applicate @XmlJavaTypeAdapter oppure serializzate il valore come stringa.

1
Compito
JAVA 25 SELF, livello 47, lezione 3
Bloccato
Progettazione della voce Libro per la Biblioteca Digitale 📖
Progettazione della voce Libro per la Biblioteca Digitale 📖
1
Compito
JAVA 25 SELF, livello 47, lezione 3
Bloccato
Caricamento del libro da XML nell'applicazione 📥
Caricamento del libro da XML nell'applicazione 📥
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION