CodeGym /Cours /JAVA 25 SELF /Manipulation de XML avec JAXB : bases et annotations

Manipulation de XML avec JAXB : bases et annotations

JAVA 25 SELF
Niveau 47 , Leçon 3
Disponible

1. Introduction à JAXB

JAXB (Java Architecture for XML Binding) — est une technologie Java standard pour la conversion (binding) d’objets Java en XML et inversement. Avec JAXB, vous pouvez facilement sérialiser des objets dans des fichiers XML, puis les reconstruire à partir de ces fichiers.

JAXB faisait partie de la bibliothèque standard de Java jusqu’à la version 11 incluse. À partir de Java 11, JAXB a été extrait dans un module séparé, qu’il faut ajouter via Maven/Gradle ou télécharger manuellement. Pour les versions modernes de Java, ajoutez les dépendances :

<!-- Exemple pour 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>

Pourquoi a-t-on besoin d’XML ?

  • XML est un format universel, lisible par l’humain, largement utilisé pour l’échange de données entre systèmes, la configuration et le stockage d’informations.
  • Contrairement à la sérialisation binaire, XML se lit facilement à l’œil, peut être validé par un schéma et ouvert dans un navigateur.

2. Classes et annotations principales de JAXB

JAXB fonctionne à l’aide d’annotations qui marquent les classes et leurs champs afin de piloter le processus de sérialisation/désérialisation.

Annotations clés

Annotation À quoi sert-elle
@XmlRootElement
Désigne l’élément racine XML (la classe elle-même)
@XmlElement
Marque un champ/propriété comme élément XML
@XmlAttribute
Marque un champ/propriété comme attribut XML
@XmlType
Contrôle l’ordre des éléments, le nom de type, etc.
@XmlTransient
Exclut le champ de la sérialisation

Classes principales

  • JAXBContext — point d’entrée, crée un contexte pour la sérialisation/désérialisation de classes données.
  • Marshaller — convertit un objet en XML (marshalling, marshal()).
  • Unmarshaller — convertit du XML en objet (unmarshalling, unmarshal()).

3. Exemple : sérialiser un objet en XML

Créons la classe que nous allons sérialiser. Disons un personnage pour notre jeu :

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() {} // Constructeur vide obligatoire !

    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 devient l’élément racine <player>.
  • @XmlElement — le champ sera un élément XML distinct (<name>, <level>).
  • @XmlAttribute — le champ sera un attribut de l’élément racine (health="100").
  • N’oubliez pas le constructeur sans argument ! JAXB l’exige pour la désérialisation.

Sérialiser un objet en 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); // Sortie formatée

        marshaller.marshal(player, System.out); // Écrire le XML dans la console
        // marshaller.marshal(player, new File("player.xml")); // Ou dans un fichier
    }
}

Résultat :

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

Désérialiser un objet depuis du 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() + ", niveau : " + player.getLevel() + ", santé : " + player.getHealth());
    }
}

4. Particularités et limites de JAXB

Exigences pour les classes

  • Constructeur public sans paramètre — obligatoire.
  • Utilisez des getters et setters pour un fonctionnement correct.
  • Tous les champs sérialisables doivent être accessibles (via l’API publique).
  • Les objets imbriqués et les collections doivent eux aussi être sérialisables (annotez-les et ajoutez un constructeur vide).

Gestion des collections et objets imbriqués

Supposons que le joueur possède un inventaire (liste d’objets). Comment sérialiser une collection ?

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

@XmlRootElement(name = "player")
public class Player {
    // ... autres champs

    private List<String> inventory;

    public Player() {}

    // ... autres getters/setters

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

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

Résultat de la sérialisation :

<player health="100">
    <name>Aragorn</name>
    <level>5</level>
    <inventory>
        <item>Sword</item>
        <item>Shield</item>
    </inventory>
</player>
  • @XmlElementWrapper — crée un « conteneur » autour de la collection (l’élément <inventory>).
  • @XmlElement(name = "item") — chaque élément de la liste est sérialisé en <item>.

Si vous avez des objets imbriqués (par exemple Position), vous devez également les annoter et leur ajouter un constructeur vide.

5. Pratique : sérialiser et désérialiser un objet 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 omis pour la concision
}

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

Sérialisation :

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

Résultat 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>

Désérialisation fonctionne de la même manière : JAXB analysera automatiquement les objets imbriqués et les collections si les classes sont correctement décrites.

6. Tableau : principales annotations JAXB et leur effet

Annotation Où l’utiliser Effet dans le XML
@XmlRootElement
Classe Élément racine
@XmlElement
Getter/champ Élément dans le XML
@XmlAttribute
Getter/champ Attribut de l’élément
@XmlElementWrapper
Getter de collection « Conteneur » de collection (par exemple, <list>)
@XmlTransient
Champ/getter Exclut le champ de la sérialisation
@XmlType
Classe Contrôle l’ordre des éléments, le nom du type

7. Particularités et limites de JAXB

Ordre des éléments

Par défaut, JAXB peut émettre les éléments par ordre alphabétique. Pour définir explicitement l’ordre, utilisez @XmlType et la propriété propOrder :

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

Exclusion de champs

Pour ne pas sérialiser un champ/getter, utilisez @XmlTransient :

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

Problèmes avec les collections

  • N’utilisez pas de collections « brutes » sans génériques : écrivez List<Type> et non List.
  • Si la collection contient des objets, leurs classes doivent également être annotées et posséder un constructeur vide.

Erreurs

  • Absence de constructeur vide — vous obtiendrez une JAXBException lors de l’unmarshalling.
  • Classe imbriquée non annotée — JAXB ne pourra pas la sérialiser/désérialiser.
  • Types non standard (par exemple, LocalDate) nécessitent un adaptateur (@XmlJavaTypeAdapter).

8. Erreurs courantes avec JAXB

Erreur n°1 : Absence de constructeur vide. JAXB exige qu’une classe sérialisable possède un constructeur public sans paramètres. À défaut, une exception JAXBException sera levée lors de l’unmarshalling.

Erreur n°2 : Objets imbriqués non annotés. Si vous avez un champ-objet, mais que sa classe n’est pas annotée @XmlRootElement ou au moins @XmlType, JAXB ne pourra pas le sérialiser/désérialiser correctement.

Erreur n°3 : Problèmes avec les collections. JAXB ne comprend pas les collections « brutes » sans indication du type des éléments. Utilisez des génériques et annotez correctement les collections (@XmlElementWrapper + @XmlElement).

Erreur n°4 : Ordre des éléments non maîtrisé. Si l’ordre des éléments dans le XML est important pour l’intégration, utilisez @XmlType avec propOrder ; sinon, JAXB peut émettre les éléments dans un autre ordre (p. ex. alphabétique).

Erreur n°5 : Utilisation de types non standard sans adaptateur. JAXB ne sait pas sérialiser certains types (par exemple, LocalDate) sans adaptateur. Utilisez @XmlJavaTypeAdapter ou sérialisez la valeur comme une chaîne.

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