Niveau : | Intermédiaire |
JAXB est une spécification qui permet de faire correspondre un document XML à un ensemble de classes et vice versa au moyen d'opérations de sérialisation/désérialisation nommées marshalling/unmarshalling.
JAXB permet aux développeurs de manipuler un document XML sans avoir à connaître XML ou la façon dont un document XML est traité comme cela est le cas avec SAX, DOM ou StAX. La manipulation du document XML se fait en utilisant des objets précédemment générés à partir d'une DTD pour JAXB 1.0 et d'un schéma XML du document à traiter pour JAXB 2.0.
Le page officiel de JAXB est à l'url : http://www.oracle.com/technetwork/articles/javase/index-140168.phpl
Ce chapitre contient plusieurs sections :
JAXB est l'acronyme de Java Architecture for XML Binding.
Le but de l'API et des spécifications JAXB est de faciliter la manipulation d'un document XML en générant un ensemble de classes qui fournissent un niveau d'abstraction plus élevé que l'utilisation de JAXP (SAX ou DOM). Avec ces deux API, toute la logique de traitements des données contenues dans le document est à écrire.
JAXB au contraire fournit un outil qui analyse un schéma XML et génère à partir de ce dernier un ensemble de classes qui vont encapsuler les traitements de manipulation du document.
Le grand avantage est de fournir au développeur un moyen de manipuler un document XML sans connaître XML ou les technologies d'analyse. Toutes les manipulations se font au travers d'objets Java.
Ces classes sont utilisées pour faire correspondre le document XML avec des instances de ces classes et vice versa : ces opérations se nomment respectivement unmarshalling et marshalling.
L'implémentation de référence de JAXB v1.0 est fournie avec le JSWDK 1.1.
Les exemples de ce chapitre utilisent cette implémentation de référence et le fichier XML suivant :
Exemple : |
<bibliotheque>
<livre>
<titre>titre 1</titre>
<auteur>auteur 1</auteur>
<editeur>editeur 1</editeur>
</livre>
<livre>
<titre>titre 2</titre>
<auteur>auteur 2</auteur>
<editeur>editeur 2</editeur>
</livre>
<livre>
<titre>titre 3</titre>
<auteur>auteur 3</auteur>
<editeur>editeur 3</editeur>
</livre>
</bibliotheque>
Le schéma XML correspondant à ce fichier XML est le suivant :
Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="bibliotheque">
<xs:complexType>
<xs:sequence>
<xs:element ref="livre" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="livre">
<xs:complexType>
<xs:sequence>
<xs:element ref="titre"/>
<xs:element ref="auteur"/>
<xs:element ref="editeur"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="titre" type="xs:string"/>
<xs:element name="auteur" type="xs:string"/>
<xs:element name="editeur" type="xs:string"/>
</xs:schema>
L'outil xjc permet d'analyser un schéma XML et de générer les interfaces et les classes qui vont permettre la manipulation d'un document XML qui respecte ce schéma. Cette opération se nomme binding.
La syntaxe de cet outil est très simple :
xjc [options] schema
schema est le nom d'un fichier contenant le schéma XML.
Les principales options sont les suivantes :
Exemple : |
C:\java\jaxb>xjc test.xsd
parsing a schema...
compiling a schema...
generated\impl\AuteurImpl.java
generated\impl\BibliothequeImpl.java
generated\impl\BibliothequeTypeImpl.java
generated\impl\EditeurImpl.java
generated\impl\LivreImpl.java
generated\impl\LivreTypeImpl.java
generated\impl\TitreImpl.java
generated\Auteur.java
generated\Bibliotheque.java
generated\BibliothequeType.java
generated\Editeur.java
generated\Livre.java
generated\LivreType.java
generated\ObjectFactory.java
generated\Titre.java
generated\bgm.ser
generated\jaxb.properties
L'exécution de la commande de l'exemple génère les fichiers suivants :
Generated
Auteur.java
bgm.ser
Bibliotheque.java
BibliothequeType.java
Editeur.java
jaxb.properties
Livre.java
LivreType.java
ObjectFactory.java
Titre.java
generated\impl
AuteurImpl.java
BibliothequeImpl.java
BibliothequeTypeImpl.java
EditeurImpl.java
LivreImpl.java
LivreTypeImpl.java
TitreImpl.java
Sans précision, les fichiers générés le sont dans le répertoire "Generated".
Pour préciser un autre répertoire, il faut utiliser l'option -d :
xjc -d sources test.xsd
Les classes et interfaces sont générées dans le répertoire "sources/generated"
Si le répertoire précisé n'existe pas, une exception est levée.
Exemple : |
C:\java\jaxb>xjc -d sources test.xsd
parsing a schema...
compiling a schema...
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
Pour éviter l'utilisation du répertoire "generated", il faut préciser un package pour les entités générées en utilisant l'option -p.
Exemple : |
C:\java\jaxb>xjc -d sources -p com.jmdoudoux.test.jaxb test.xsd
parsing a schema...
compiling a schema...
com\moi\test\jaxb\Auteur.java
com\moi\test\jaxb\Bibliotheque.java
com\moi\test\jaxb\BibliothequeType.java
com\moi\test\jaxb\Editeur.java
com\moi\test\jaxb\Livre.java
com\moi\test\jaxb\LivreType.java
com\moi\test\jaxb\ObjectFactory.java
com\moi\test\jaxb\Titre.java
com\moi\test\jaxb\jaxb.properties
com\moi\test\jaxb\bgm.ser
com\moi\test\jaxb\impl\AuteurImpl.java
com\moi\test\jaxb\impl\BibliothequeImpl.java
com\moi\test\jaxb\impl\BibliothequeTypeImpl.java
com\moi\test\jaxb\impl\EditeurImpl.java
com\moi\test\jaxb\impl\LivreImpl.java
com\moi\test\jaxb\impl\LivreTypeImpl.java
com\moi\test\jaxb\impl\TitreImpl.java
Un objet de type factory et des interfaces pour chacun des éléments qui composent le document sont définis.
Pour chaque élément qui peut contenir d'autres éléments, des interfaces de XXXType sont créées (BibliothequeType et LivreType dans l'exemple).
L'interface BibliothequeType définit simplement un getter sur une collection qui contiendra tous les livres.
L'interface LivreType définit des getters et des setters sur les éléments auteur, editeur et titre.
Les interfaces Titre, Editeur et Auteur définissent un getter et un setter sur les valeurs des données que ces éléments contiennent.
La classe ObjectFactory permet de créer des instances des différentes entités définies.
Le répertoire impl contient les classes qui implémentent ces interfaces. Ces classes sont spécifiques à l'implémentation des spécifications JAXB utilisée.
Pour pouvoir utiliser ces interfaces et ces classes, il faut les compiler en incluant au classpath, tous les fichiers .jar contenus dans le répertoire lib de l'implémentation de JAXB.
L'API JAXB propose un framework composé de classes regroupées dans trois packages :
Pour pouvoir utiliser JAXP, il faut tout d'abord obtenir un objet de type JAXBContext qui est le point d'entrée pour utiliser l'API. Il faut ensuite utiliser la méthode newInstance() qui attend en paramètre le nom du package contennant les interfaces générées (celui fournit au paramètre -p de la commande xjc).
Pour pouvoir créer en mémoire les objets qui représentent le document XML, il faut à partir de l'instance du type JAXBContext, appeler la méthode createUnmarshaller() qui renvoie un objet de type Unmarshaller.
L'appel de la méthode unmarshal() permet de créer les différents objets.
Pour parcourir le document, il suffit d'utiliser les différents objets instanciés.
Exemple : |
package com.jmdoudoux.test.jaxb;
import javax.xml.bind.*;
import java.io.*;
import java.util.*;
public class TestJAXB {
public static void main(String[] args) {
try {
JAXBContext jc = JAXBContext.newInstance("com.jmdoudoux.test.jaxb");
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setValidating(true);
Bibliotheque bibliotheque = (Bibliotheque) unmarshaller.unmarshal(new File("test.xml"));
List livres = bibliotheque.getLivre();
for (int i = 0; i < livres.size(); i++) {
LivreType livre = (LivreType) livres.get(i);
System.out.println("Livre ");
System.out.println("Titre : " + livre.getTitre());
System.out.println("Auteur : " + livre.getAuteur());
System.out.println("Editeur : " + livre.getEditeur());
System.out.println();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Résultat : |
Livre
Titre : titre 1
Auteur : auteur 1
Editeur : editeur 1
Livre
Titre : titre 2
Auteur : auteur 2
Editeur : editeur 2
Livre
Titre : titre 3
Auteur : auteur 3
Editeur : editeur 3
Parmi les classes générées à partir du schéma XML, il y a la classe ObjectFactory qui permet de créer des instances des autres classes générées.
Exemple : |
import javax.xml.bind.*;
import java.io.*;
import java.util.*;
public class TestJAXB2 {
public static void main(String[] args) {
try {
ObjectFactory objFactory = new ObjectFactory();
Bibliotheque bibliotheque = (Bibliotheque) objFactory.createBibliotheque();
List livres = bibliotheque.getLivre();
for (int i = 1; i < 4; i++) {
LivreType livreType = objFactory.createLivreType();
// LivreType livre = objFactory.createLivreType();
livreType.setAuteur("Auteur" + i);
livreType.setEditeur("Editeur" + i);
livreType.setTitre("Titre" + i);
livres.add(livreType);
}
} catch (Exception e) {
}
}
}
Une fois la représentation en mémoire du document XML créée ou modifiée, il est fréquent de devoir l'envoyer dans un flux tel qu'un fichier pour conserver les modifications. Cette opération se nomme marshalling.
Pour réaliser cette opération, il faut tout d'abord obtenir un objet du type JAXBContext en utilisant la méthode newInstance(). Cette méthode demande en paramètre une chaîne de caractères indiquant le package des interfaces générées à partir du schéma.
La méthode createMarshaller() permet d'obtenir un objet de type Marshaller. C'est cet objet qui va formater le document XML.
Il est possible de lui préciser des propriétés pour effectuer sa tâche en utilisant la méthode setProperty(). Des constantes sont définies pour ces propriétés dont les principales sont :
Les propriétés doivent être des objets.
L'appel de la méthode marshal() formate le document dont l'objet racine est fourni en premier paramètre. Il existe plusieurs surcharges de cette méthode pour préciser où est envoyé le résultat de la génération. Le second paramètre permet de préciser cette cible : un flux en sortie, un arbre DOM, des événements SAX.
Exemple : |
package com.jmdoudoux.test.jaxb;
import javax.xml.bind.*;
import java.io.*;
import java.util.*;
public class TestJAXB3 {
public static void main(String[] args) {
try {
ObjectFactory objFactory = new ObjectFactory();
Bibliotheque bibliotheque = (Bibliotheque) objFactory.createBibliotheque();
List livres = bibliotheque.getLivre();
for (int i = 1; i < 4; i++) {
LivreType livreType = objFactory.createLivreType();
// LivreType livre = objFactory.createLivreType();
livreType.setAuteur("Auteur" + i);
livreType.setEditeur("Editeur" + i);
livreType.setTitre("Titre" + i);
livres.add(livreType);
}
JAXBContext jaxbContext = JAXBContext.newInstance("com.jmdoudoux.test.jaxb");
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, new Boolean(true));
Validator validator = jaxbContext.createValidator();
marshaller.marshal(bibliotheque, System.out);
} catch (Exception e) {
}
}
}
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bibliotheque>
<livre>
<titre>Titre1</titre>
<auteur>Auteur1</auteur>
<editeur>Editeur1</editeur>
</livre>
<livre>
<titre>Titre2</titre>
<auteur>Auteur2</auteur>
<editeur>Editeur2</editeur>
</livre>
<livre>
<titre>Titre3</titre>
<auteur>Auteur3</auteur>
<editeur>Editeur3</editeur>
</livre>
</bibliotheque>
JAXB 2.0 a été développé sous la JSR 222 et est incorporée dans Java EE 5 et dans Java SE 6.
Les fonctionnalités de JAXB 2.0 par rapport à JAXB 1.0 sont :
En plus de son utilité principale, JAXB 2.0 propose d'atteindre plusieurs objectifs :
L'utilisation de JAXB implique généralement deux étapes :
JAXB 2.0 permet toujours de mapper des objets Java dans un document XML et vice versa. Il permet aussi de générer des classes Java à partir un schéma XML et inversement.
La sérialisation d'un graphe d'objets Java est effectuée par une opération de mashalling. L'opération inverse est dite unmashalling. Lors de ces deux opérations, le document XML peut être validé.
JAXB 2.0 utilise de nombreuses annotations définies dans le package javax.xml.bin.annotation essentiellement pour préciser le mode de fonctionnement lors des opérations de marshalling/unmarshalling.
Ces annotations précisent le mapping entre les classes Java et le document XML. La plupart de ces annotations ont des valeurs par défaut ce qui réduit l'obligation de leur utilisation si la valeur par défaut correspond au besoin.
JAXB 2.0 permet aussi de réaliser dynamiquement à l'exécution une transformation d'un graphe d'objets en document XML et vice versa. C'est cette fonctionnalité qui est largement utilisée dans les services web grâce à l'API JAX-WS 2.0.
La classe abstraite JAXBContext fournie par l'API JAXB permet de gérer la transformation d'objets Java en XML et vice versa.
JAXB 2.0 propose plusieurs outils pour faciliter sa mise en oeuvre :
L'API JAXB est contenue dans la package javax.xml.bind
JAXB 2.0 est incorporée dans Java EE 5 et dans Java SE 6.
Pour les versions antérieures de ces plate-formes, il est possible d'utiliser le Java Web Services Developer Pack 2.0 (JWSDP 2.0) qui contient l'implémentation de référence de JAXB 2.0.
Avec la version fournie avec JWSDP 2.0, les bibliothèques suivantes doivent être ajoutées au classpath :
jaxb\lib\jaxb-api.jar,
jaxb\lib\jaxb-impl.jar,
jaxb\lib\jaxb-xjc.jar,
jwsdp-shared\lib\activation.jar,
sjsxp\lib\jsr173_api.jar,
sjsxp\lib\sjsxp.jar
Attention JAXB 2.0 requiert un JDK 5.0 minimum pour être utilisé.
La mise en oeuvre de JAXB requiert pour un usage standard plusieurs étapes :
L'utilisation de JAXB se fait donc en deux phases :
L'utilisation de JAXB met en oeuvre plusieurs éléments :
Les avantages d'utiliser JAXB sont nombreux :
Pour permettre l'utilisation et la manipulation d'un document XML, JAXB propose de générer un ensemble de classes à partir du schéma XML du document.
Chaque implémentation de JAXB doit fournir un outil (binding compiler) qui permet la génération de classes et interfaces à partir d'un schema (binding a schema).
L'implémentation de référence fournit l'outil xjc pour générer les classes à partir d'un schéma XML.
L'utilisation la plus simple de l'outil xjc est de lui fournir le fichier qui contient le schéma XML du document à utiliser.
Exemple : |
xjc biblio.xsd
L'outil xjc possède plusieurs options dont voici les principales :
Option |
Rôle |
-p nom_package |
Précise le package qui va contenir les classes générées |
-d répertoire |
Précise le répertoire qui va contenir les classes générées |
-nv |
Inhibe la validation du schéma |
-b fichier |
Précise un fichier de configuration |
-classpath chemin |
Précise le classpath |
Le compilateur génère des classes en correspondance avec le schéma XML fourni à l'outil.
Exemple : biblio.xsd |
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.jmdoudoux.com/test/jaxb"
xmlns:tns="http://www.jmdoudoux.com/test/jaxb"
elementFormDefault="qualified">
<xs:element name="bibliotheque">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="livre">
<xs:complexType>
<xs:sequence>
<xs:element name="titre" type="xs:string" />
<xs:element name="auteur">
<xs:complexType>
<xs:sequence>
<xs:element name="nom" type="xs:string" />
<xs:element name="prenom" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="editeur" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Exemple : exécution de la commande xjc |
C:\Documents and Settings\jmd\workspace\TestJAXB>xjc -d src biblio.xsd
parsing a schema...
compiling a schema...
com\jmdoudoux\test\jaxb\Bibliotheque.java
com\jmdoudoux\test\jaxb\ObjectFactory.java
com\jmdoudoux\test\jaxb\package-info.java
Trois entités sont générées dans le répertoire src :
Par défaut, le package utilisé est déduit de l'espace de nommage défini dans le schéma.
Chaque classe qui encapsule un type complexe du schéma possède des getters et setters sur les éléments du schéma.
La fabrique permet de créer des instances de chacun des types d'objets correspondant à un type complexe du schéma. Cette fabrique est particulièrement utile lors de la création d'un nouveau document XML : le graphe d'objets est créé en ajoutant des instances des objets retournés par cette fabrique.
Les classes générées sont dépendantes de l'implémentation de JAXB utilisée : il est préférable d'utiliser les classes générées par une implémentation avec cette implémentation.
Par défaut, JAXB utilise des règles pour définir chaque entité incluse dans le schéma (element et complexType définis dans le schéma).
JAXB fournit une API qui permet à l'exécution d'effectuer les opérations de transformation d'un document XML en un graphe d'objets utilisant les classes générées et vice versa (unmashalling/marshalling) ainsi que des opérations de validation.
L'objet principal pour les opérations de transformation est l'objet JAXBContext : il permet d'utiliser l'API JAXB. Pour obtenir une instance de cet objet, il faut utiliser la méthode statique newInstance() en lui passant en paramètre le ou les packages contenant les classes générées à utiliser. Dans le cas où plusieurs packages doivent être précisés, il faut les séparer par une virgule.
Une autre surcharge de la méthode newInstance() attend en paramètre la classe qui encapsule la racine du document.
Exemple : |
JAXBContext jc = JAXBContext.newInstance("com.jmdoudoux.test.jaxb");
L'API JAXB propose de transformer un document XML en un ensemble d'objets qui vont encapsuler les données et la hiérarchie du document. Ces objets sont des instances des classes générées à partir du schéma XML.
La création des objets nécessite la création d'un objet de type JAXBContext en utilisant la méthode statique newInstance().
Il faut ensuite instancier un objet de type Unmarshaller qui va permettre de transformer un document XML en un ensemble d'objets. Un telle instance est obtenue en utilisant la méthode createUnmarshaller() de la classe JAXBContext.
Exemple : |
Unmarshaller unmarshaller = jc.createUnmarshaller();
La méthode unmarshal() de la classe Unmarshaller se charge de traiter un document XML et retourne un objet du type complexe qui encapsule la racine du document XML. Elle possède de nombreuses surcharges à utiliser en fonction des besoins.
Exemple : |
Bibliotheque bibliotheque = (Bibliotheque) unmarshaller.unmarshal(new File("biblio.xml"));
A partir de cet objet, il est possible d'obtenir et de modifier des données encapsulées dans les différents objets créés à partir des classes générées et du contenu du document. Chacun de ces objets possède un getter et un setter sur leur noeud direct.
Exemple : |
List<Livre> livres = bibliotheque.getLivre();
for (int i = 0; i < livres.size(); i++) {
Livre livre = livres.get(i);
System.out.println("Livre ");
System.out.println("Titre : " + livre.getTitre());
System.out.println("Auteur : " + livre.getAuteur().getNom()
+ " " +livre.getAuteur().getPrenom());
System.out.println("Editeur : " + livre.getEditeur());
System.out.println();
Il est possible de demander la validation du document avec le schéma en utilisant la méthode setValidating() de la classe Unmarshaller.
Exemple : |
unmarshaller.setValidating(true);
Exemple : mise en oeuvre des entités générées à partir du schéma biblio.xsd |
package com.jmdoudoux.test.jaxb;
import java.io.File;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import com.jmdoudoux.test.jaxb.Bibliotheque.Livre;
public class TestJAXB2 {
public static void main(String[] args) {
try {
JAXBContext jc = JAXBContext.newInstance("com.jmdoudoux.test.jaxb");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Bibliotheque bibliotheque = (Bibliotheque) unmarshaller.unmarshal(
new File("biblio.xml"));
List<Livre> livres = bibliotheque.getLivre();
for (int i = 0; i < livres.size(); i++) {
Livre livre = livres.get(i);
System.out.println("Livre ");
System.out.println("Titre : " + livre.getTitre());
System.out.println("Auteur : " + livre.getAuteur().getNom()
+ " " +livre.getAuteur().getPrenom());
System.out.println("Editeur : " + livre.getEditeur());
System.out.println();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Exemple : le document XML utilisé |
<?xml version="1.0" encoding="UTF-8"?>
<tns:bibliotheque xmlns:tns="http://www.jmdoudoux.com/test/jaxb"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jmdoudoux.com/test/jaxb biblio.xsd ">
<tns:livre>
<tns:titre>titre 1</tns:titre>
<tns:auteur>
<tns:nom>nom 1</tns:nom>
<tns:prenom>prenom 1</tns:prenom>
</tns:auteur>
<tns:editeur>editeur 1</tns:editeur>
</tns:livre>
<tns:livre>
<tns:titre>titre 2</tns:titre>
<tns:auteur>
<tns:nom>nom 2</tns:nom>
<tns:prenom>prenom 2</tns:prenom>
</tns:auteur>
<tns:editeur>editeur 2</tns:editeur>
</tns:livre>
<tns:livre>
<tns:titre>titre 3</tns:titre>
<tns:auteur>
<tns:nom>nom 3</tns:nom>
<tns:prenom>prenom 3</tns:prenom>
</tns:auteur>
<tns:editeur>editeur 3</tns:editeur>
</tns:livre>
</tns:bibliotheque>
Résultat : |
Livre
Titre : titre 1
Auteur : nom 1 prenom 1
Editeur : editeur 1
Livre
Titre : titre 2
Auteur : nom 2 prenom 2
Editeur : editeur 2
Livre
Titre : titre 3
Auteur : nom 3 prenom 3
Editeur : editeur 3
JAXB permet de créer un document XML à partir d'un graphe d'objets : cette opération est nommée marshalling. Une opération de mashalling est l'opération inverse de l'opération d'unmarshalling.
Ce graphe d'objets peut être issu d'une opération de type unmarshalling (construction à partir d'un document XML existant) ou issu d'une création de toutes pièces de l'ensemble des objets. Dans le premier cas cela correspond à une modification du document et dans le second cas à une création de document.
La création des objets nécessite la création d'un objet de type JAXBContext en utilisant la méthode statique newInstance().
Il faut ensuite instancier un objet de type Marshaller qui va permettre de transformerun ensemble d'objets en un document XML. Une telle instance est obtenue en utilisant la méthode createMarshaller() de la classe JAXBContext.
Exemple : |
Marshaller marshaller = jc.createMarshaller();
La méthode marshal() de la classe Marshaller se charge de créer un document XML à partir d'un graphe d'objets dont l'objet racine lui est fourni en paramètre.
La méthode marshal() possède plusieurs surcharges qui permettent de préciser la forme du document XML généré :
L'objet Mashaller possède des propriétés qu'il est possible de valoriser en utilisant la méthode setProperty(). Les spécifications de JAXB proposent des propriétés qui doivent être obligatoirement supportées par l'implémentation. Chaque implémentation est libre de proposer des propriétés supplémentaires.
Exemple : demander le formattage du document créé |
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Il est possible de demander la validation du graphe d'objets. La validation n'est pas intégrée à l'opération de mashalling mais elle est effectuée à la demande.
La validation s'effectue en utilisant la classe Validator. Une instance de cette classe est obtenue en utilisation la méthode createValidator() de la classe JAXBContext.
Exemple : |
Validator validator = jaxbContext.createValidator();
Pour valider le graphe d'objets vis-à-vis du schéma, il faut utiliser la méthode validate() de la classe Validator en lui passant en paramètre l'objet qui encapsule la racine du document.
JAXB permet de mapper une ou plusieurs classes annotées vers un document XML sans être obligé d'utiliser un schéma XML. Dans ce cas, le développeur a la charge d'écrire la ou les classes annotées requises.
Il n'est donc pas nécessaire d'utiliser des classes générées par le compilateur de schéma mais dans ce cas, la ou les classes doivent être créées manuellement en utilisant les annotations adéquates.
La classe qui encapsule la racine du document doit être annotée avec l'annotation @XmlRootElement. Une exception de type javax.xml.bind.MarshalException est levée par JAXB si la classe racine ne possède pas cette annotation.
Exemple : |
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "com.jmdoudoux.test.jaxb.
Personne" as an element because it is missing an @XmlRootElement annotation]
JAXB utilise des comportements par défaut qui ne nécessitent de la part du développeur que de définir des comportements particuliers si les valeurs par défaut ne conviennent pas.
Exemple : un bean utilisé avec JAXB |
package com.jmdoudoux.test.jaxb;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Personne {
private String nom;
private String prenom;
private int taille;
private Date dateNaiss;
public Personne() {
}
public Personne(String nom, String prenom, int taille, Date dateNaiss) {
super();
this.nom = nom;
this.prenom = prenom;
this.taille = taille;
this.dateNaiss = dateNaiss;
}
public Date getDateNaiss() {
return dateNaiss;
}
public void setDateNaiss(Date dateNaiss) {
this.dateNaiss = dateNaiss;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public int getTaille() {
return taille;
}
public void setTaille(int taille) {
this.taille = taille;
}
}
La création du document nécessite la création d'un objet de type JAXBContext en utilisant la méthode statique newInstance().
Il faut ensuite créer un objet de type Marshaller à partir du contexte et appeler sa méthode marshal() pour générer le document.
Exemple : marshalling de l'objet |
package com.jmdoudoux.test.jaxb;
import java.util.Date;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class TestJAXB1 {
public static void main(String[] args) {
try {
JAXBContext context = JAXBContext.newInstance(Personne.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Personne p = new Personne("nom1", "prenom1", 175, new Date());
m.marshal(p, System.out);
} catch (JAXBException ex) {
ex.printStackTrace();
}
}
}
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<personne>
<dateNaiss>2007-01-16T17:03:31.213+01:00</dateNaiss>
<nom>nom1</nom>
<prenom>prenom1</prenom>
<taille>175</taille>
</personne>
Une des classes générées à partir du schéma se nomme ObjectFactory : c'est une fabrique d'objets pour les classes générées qui encapsulent des données d'un document respectant le schéma.
Pour créer un document XML en utilisant ces classes, il faut mettre en oeuvre plusieurs étapes.
La création du document nécessite la création d'un objet de type JAXBContext en utilisant la méthode statique newInstance().
Il faut ensuite créer le graphe d'objets en utilisant la classe ObjectFactory pour instancier les différents objets et valoriser les données de ces objets en utilisant les setters.
Il faut créer un objet de type Marshaller à partir du contexte et appeler sa méthode marshal() pour générer le document.
Exemple : |
package com.jmdoudoux.test.jaxb;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class TestJAXB3 {
public static void main(String[] args) {
try {
JAXBContext context = JAXBContext.newInstance(Bibliotheque.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
ObjectFactory fabrique = new ObjectFactory();
Bibliotheque bibliotheque = fabrique.createBibliotheque();
Bibliotheque.Livre livre = fabrique.createBibliothequeLivre();
livre.setEditeur("editeur 1");
livre.setTitre("titre 1");
Bibliotheque.Livre.Auteur auteur = fabrique
.createBibliothequeLivreAuteur();
auteur.setNom("nom 1");
auteur.setPrenom("prenom 1");
livre.setAuteur(auteur);
bibliotheque.getLivre().add(livre);
m.marshal(bibliotheque, System.out);
} catch (JAXBException ex) {
ex.printStackTrace();
}
}
}
JAXB propose des fonctionnalités pour configurer finement les traitements qu'il propose.
JAXB utilise des traitements par défaut qu'il est possible de configurer différemment en utilisant soit les annotations soit un fichier de configuration qui sera fourni au compilateur.
Les classes générées peuvent aussi être configurées, notamment le nom du package et des classes utilisées.
Par défaut, le générateur de classes à partir du schéma utilise des conventions de nommage des différentes entités générées à partir des noms utilisés dans le schéma. Il est possible de configurer de façon différente les noms utilisés.
Les spécifications JAXB décrivent de façon précise comment les éléments d'un schéma sont transformés en classes Java. Il est possible de préciser des informations particulières dans le schéma pour modifier ce comportement par défaut.
Ces informations peuvent être incluses directement dans le schéma ou fournies dans un fichier dédié. Dans le schéma, ces informations sont fournies dans un tag <annotation> qui contient un tag fils <appinfo>.
Exemple : biblio2.xsd |
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.jmdoudoux.com/test/jaxb/perso"
xmlns:tns="http://www.jmdoudoux.com/test/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="1.0"
elementFormDefault="qualified">
<xs:element name="bibliotheque">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="livre">
<xs:complexType>
<xs:sequence>
<xs:element name="titre" type="xs:string" />
<xs:element name="auteur">
<xs:complexType>
<xs:sequence>
<xs:element name="nom"
type="xs:string" />
<xs:element name="prenom"
type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="editeur" type="xs:string">
<!--
Personnalisation de la propriété editeur en nomEditeur
-->
<xs:annotation>
<xs:appinfo>
<jaxb:property name="nomEditeur" />
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Exemple : génération des classes à partir du schéma annoté |
C:\Documents and Settings\jmd\workspace\TestJAXB>xjc -d src -extension biblio2.x
sd
parsing a schema...
compiling a schema...
com\jmdoudoux\test\jaxb\perso\Bibliotheque.java
com\jmdoudoux\test\jaxb\perso\ObjectFactory.java
com\jmdoudoux\test\jaxb\perso\package-info.java
L'option -extension du générateur de classes autorise l'outil à utiliser des extensions proposées par l'implémentation de JAXB. Sans cette option, l'outil utilise le mode strict et une exception est levée si une extension est utilisée.
Exemple : la classe Livre générée à partir du schéma |
...
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"titre",
"auteur",
"nomEditeur"
})
public static class Livre {
@XmlElement(required = true)
protected String titre;
@XmlElement(required = true)
protected Bibliotheque.Livre.Auteur auteur;
@XmlElement(name = "editeur", required = true)
protected String nomEditeur;
...
/**
* Gets the value of the nomEditeur property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getNomEditeur() {
return nomEditeur;
}
/**
* Sets the value of the nomEditeur property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setNomEditeur(String value) {
this.nomEditeur = value;
}
...
JAXB propose de nombreuses fonctionnalités de configuration : consultez les spécifications pour de plus amples détails.
La configuration de la transformation d'un document XML en objets Java est réalisée grâce à l'utilisation d'annotations dédiées dans les classes Java.
De nombreuses annotations sont définies par JAXB 2.0 dont voici les principales :
XmlAccessorOrder |
Ordonner les champs et propriétés dans la classe |
XmlAccessorType |
Préciser comment un champ ou une propriété est accédé par JAXB. Par défaut, tous les champs public ou annotés sont pris en compte sauf ceux marqués avec @XmlTransient. Sa valeur est de type XmlAccessType qui est une énumération qui peut prendre les valeurs NONE, FIELD, PROPERTY, PUBLIC_MEMBER |
XmlAnyAttribute |
|
XmlAnyElement |
Convertir une collections d'éléments en une collection de type List<Element> |
XmlAttachmentRef |
|
XmlAttribute |
Convertir une propriété en un attribut dans le document XML |
XmlElement |
Convertir une propriété en un élément dans le document XML |
XmlElementDecl |
Associer une fabrique à un élément XML |
XmlElementRef |
|
XmlElementRefs |
|
XmlElements |
Contenir plusieurs annotations @XmlElement |
XmlElementWrapper |
Créer un élément père dans le document XML pour des collections d'éléments |
XmlEnum |
Définir la façon dont une énumération est convertie dans le document XML |
XmlEnumValue |
Définir la valeur numérique d'un élément d'une énumération |
XmlID |
Convertir une propriété en un XML ID dans le document XML. Ne peut être utilisé que sur une seule propriété |
XmlIDREF |
Convertir une propriété en un XML IDREF dans le document XML |
XmlInlineBinaryData |
|
XmlList |
Préciser que les éléments de liste sont représentés sous la forme d'une chaîne de caractères dans laquelle chaque valeur est séparée par un espace |
XmlMimeType |
|
XmlMixed |
|
XmlNs |
Associer un préfixe d'un espace de nommage à un URI |
XmlRegistry |
Marquer une classe comme possédant une ou des méthodes annotées avec @XmlElementDecl |
XmlRootElement |
Associer une classe ou une énumération à un élément racine XML |
XmlSchema |
Associer un ou plusieurs espaces de nommage à un package |
XmlSchemaType |
Associer un type Java ou une énumération à un type défini dans un schéma |
XmlSchemaTypes |
|
XmlTransient |
Marquer une entité ne devant pas être mappée dans le document XML |
XmlType |
Associer une classe ou une énumération à un type d'un schéma XML. L'attribut propOrder permet de définir l'ordre des champs dans le document XML |
XmlValue |
Convertir une classe en une valeur simple |
Ces annotations sont définies dans le package javax.xml.bind.annotation.
L'annotation @XmlRootElement peut être utilisée sur une classe pour préciser que cette classe sera le tag racine du document XML. Chaque attribut de la classe sera un tag fils dans le document XML. L'attribut namespace de l'annotation @XmlRootElement permet de préciser l'espace de nommage.
L'annotation XmlTransient permet d'ignorer une entité dans le mapping.
Exemple : |
@XmlTransient
public String getNom() {
return nom;
}
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<personne xmlns="http://www.jmdoudoux.com/test/jaxb">
<dateNaiss>2007-06-21T15:18:28.809+02:00</dateNaiss>
<prenom>prenom1</prenom>
<taille>175</taille>
</personne>
L'annotation XmlAttribute permet de mapper une propriété sous la forme d'un attribut et fournir des précisions sur la configuration de cet attribut.
Exemple : |
@XmlAttribute (name="nom")
public String getNom() {
return nom;
}
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<personne xmlns="http://www.jmdoudoux.com/test/jaxb" nom="nom1">
<dateNaiss>2007-06-21T15:20:34.377+02:00</dateNaiss>
<prenom>prenom1</prenom>
<taille>175</taille>
</personne>
Pour les collections, il est possible d'utiliser l'annotation @XmlElementWrapper pour définir un élément père qui encapsule les occurrences de la collection et d'utiliser l'annotation XmlElement pour préciser le nom de chaque élément de la collection.
Exemple : |
@XmlElementWrapper(name = "adresses")
@XmlElement(name = "adresse")
protected List<Adresse> adresses = new ArrayList<Adresse>();
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<personne xmlns="http://www.jmdoudoux.com/test/jaxb">
<adresses>
<adresse>
<codePostal>54000</codePostal>
<ligne1>ligne1</ligne1>
<ligne2>ligne2</ligne2>
<pays>pays1</pays>
<ville>ville1</ville>
</adresse>
</adresses>
<dateNaiss>2007-06-21T15:34:35.547+02:00</dateNaiss>
<nom>nom1</nom>
<prenom>prenom1</prenom>
<taille>175</taille>
</personne>
Il est possible de configurer l'ordre des éléments.
Exemple : |
@XmlRootElement
@XmlType(propOrder = {"nom", "prenom", "taille", "dateNaiss", "adresses"})
public class Personne {
private String nom;
private String prenom;
private int taille;
private Date dateNaiss;
@XmlElementWrapper(name = "adresses")
@XmlElement(name = "adresse")
protected List<Adresse> adresses = new ArrayList<Adresse>();
// getters et setters ...
}
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<personne xmlns="http://www.jmdoudoux.com/test/jaxb">
<nom>nom1</nom>
<prenom>prenom1</prenom>
<taille>175</taille>
<dateNaiss>2007-06-21T15:44:12.815+02:00</dateNaiss>
<adresses>
<adresse>
<codePostal>54000</codePostal>
<ligne1>ligne1</ligne1>
<ligne2>ligne2</ligne2>
<pays>pays1</pays>
<ville>ville1</ville>
</adresse>
</adresses>
</personne>
Il est possible de définir des classes de type Adapter qui permettent de personnaliser la façon dont un objet est sérialisé/désérialisé dans le document XML.
Ces adapters héritent de la classe javax.xml.bind.annotation.adapters.XmlAdapter. Il suffit de redéfinir les méthodes marshal() et unmashal(). Ces méthodes seront utilisées à l'exécution par l'API JAXB lors des transformations.
Exemple : la classe DateAdapter |
package com.jmdoudoux.test.jaxb;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DateAdapter extends XmlAdapter<String, Date> {
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
public Date unmarshal(String date) throws Exception {
return df.parse(date);
}
public String marshal(Date date) throws Exception {
return df.format(date);
}
}
L'annotation @XmlJavaTypeAdapter permet de préciser la classe de type Adapter qui sera à utiliser à la place de la conversion par défaut.
Exemple : |
package com.jmdoudoux.test.jaxb;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlType(propOrder = {"nom", "prenom", "taille", "dateNaiss", "adresses"})
public class Personne {
private String nom;
private String prenom;
private int taille;
private Date dateNaiss;
@XmlElementWrapper(name = "adresses")
@XmlElement(name = "adresse")
protected List<Adresse> adresses = new ArrayList<Adresse>();
public Personne() {
}
public Personne(String nom, String prenom, int taille, Date dateNaiss) {
super();
this.nom = nom;
this.prenom = prenom;
this.taille = taille;
this.dateNaiss = dateNaiss;
}
@XmlJavaTypeAdapter(DateAdapter.class)
public Date getDateNaiss() {
return dateNaiss;
}
public void setDateNaiss(Date dateNaiss) {
this.dateNaiss = dateNaiss;
}
// getters et setters ...
}
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<personne xmlns="http://www.jmdoudoux.com/test/jaxb">
<nom>nom1</nom>
<prenom>prenom1</prenom>
<taille>175</taille>
<dateNaiss>22/06/2007</dateNaiss>
<adresses>
<adresse>
<codePostal>54000</codePostal>
<ligne1>ligne1</ligne1>
<ligne2>ligne2</ligne2>
<pays>pays1</pays>
<ville>ville1</ville>
</adresse>
</adresses>
</personne>
Ceci ne présente qu'une toute petite partie des fonctionnalités proposées par JAXB. Pour de plus amples informations, consultez les spécifications de JAXB.
L'outil schemagen fourni avec l'implémentation par défaut permet de générer un schéma XML à partir de classes annotées compilées.
Exemple : |
C:\Documents and Settings\jmd\workspace\TestJAXB>schemagen -cp ./bin com.jmdoudo
ux.test.jaxb.Personne
Note: Writing schema1.xsd
Résultat : |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="personne" type="personne"/>
<xs:complexType name="personne">
<xs:sequence>
<xs:element name="nom" type="xs:string" minOccurs="0"/>
<xs:element name="prenom" type="xs:string" minOccurs="0"/>
<xs:element name="taille" type="xs:int"/>
<xs:element name="dateNaiss" type="xs:string" minOccurs="0"/>
<xs:element name="adresses" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="adresse" type="adresse" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="adresse">
<xs:sequence>
<xs:element name="codePostal" type="xs:string" minOccurs="0"/>
<xs:element name="ligne1" type="xs:string" minOccurs="0"/>
<xs:element name="ligne2" type="xs:string" minOccurs="0"/>
<xs:element name="pays" type="xs:string" minOccurs="0"/>
<xs:element name="ville" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>