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 |
|---|---|
|
Désigne l’élément racine XML (la classe elle-même) |
|
Marque un champ/propriété comme élément XML |
|
Marque un champ/propriété comme attribut XML |
|
Contrôle l’ordre des éléments, le nom de type, etc. |
|
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 |
|---|---|---|
|
Classe | Élément racine |
|
Getter/champ | Élément dans le XML |
|
Getter/champ | Attribut de l’élément |
|
Getter de collection | « Conteneur » de collection (par exemple, <list>) |
|
Champ/getter | Exclut le champ de la sérialisation |
|
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.
GO TO FULL VERSION