-
Java et XML - JAXB
Chapitres traits Qu'est ce que le Data Binding ou association de
donnes ?
Le XML est aujourd'hui un format d'change de donnes trs utilis.
Il possde de nombreux avantages : il est standard, simple, et
surtout facile lire. Il peut tre lu par un homme, mais ce qui le
plus intressant c'est qu'il peut tre lu par un ordinateur. En
effet, la puissance du XML reposesur le fait qu'il peut tre analys
par un programme et le contenu de ce flux est compris par la
machine. La structure de ce langage permet eneffet de comprendre
les relations entre toutes ces donnes.
De ce fait, les programmeurs ont ralis de nombreuses API
permettant d'accder aux donnes XML travers leurs langages favoris
(DOM et SAXpar exemple en Java). Nanmoins, ces dernires ont des
inconvnients. Tous d'abord, il faut tudier et apprendre une
nouvelle API. Puis, ils sont
trop gnraux. C'est--dire que le programmeur doit adapter le code
son application chaque fois qu'il veut accder des donnes XML.
Ensuite, il faut qu'il cre lui-mmetoutes les classes permettant de
grer ces nouvelles donnes dans son programme, cela lui prend donc
beaucoup de temps.
Pour remdier ces inconvnients, il existe le Data Binding
galement appel en franais : association de donnes. En java, Sun a
ralis une API nomme JAXB (JavaArchitecture for XML Binding) pour
simplifier les processus de transformation dobjets Java en fichier
XML, et de fichier XML en objets Java.
JAXB est une spcification qui permet de faire correspondre un
document XML un ensemble de classes et vice et versa via des
oprations de srialisation/dsrialisation
nommemarshaling/unmarshaling.
JAXB permet aux dveloppeurs de manipuler un document XML sans
avoir connatre XML ou la faon dont un document XML est traite comme
cela est le cas avec SAX, DOMou STAX.
La manipulation du document XML se fait en utilisant des objets
prcdemment gnrs partir d'une DTD pour JAXB 1.0 et d'un schma XML du
document traiter pour JAXB2.0.
Qu'est ce que le Data Binding ou association de donnes ?
Le Data Binding est une technologie permettant d'automatiser la
transformation d'un modle de donnes en un modle de donnes objets
dans un langage de programmation. Autrementdit, il permet par
exemple de convertir les fichiers XML en instances de classes
Java.
Pour raliser cela, il y a trois tapes :
1. La gnration de classes.
2. Le rassemblement des donnes.
3. La redistribution des donnes
Le schma suivant rsume assez bien le principe : un document XML
suit les rgles de grammaire du schema , ce dernier une fois compil
permet de crer une classecorrespondante. Cette dernire permettra de
crer une instance d'objet correspondant :
-
JAXB
JAXB est l'acronyme de Java Architecture for XML Binding. Le but
de l'API et des spcifications JAXB est de faciliter la manipulation
d'un document XML en gnrant un ensemble declasses qui fournissent
un niveau d'abstraction plus lev que l'utilisation de JAXP (SAX ou
DOM).
Avec ces deux API, SAX et DOM, toute la logique de traitements
des donnes contenues dans le document est crire.
JAXB au contraire fournit un outil qui analyse un schma XML et
gnre partir de ce dernier un ensemble de classes qui vont
encapsuler les traitements de manipulation dudocument. Le grand
avantage est de fournir au dveloppeur un moyen de manipuler un
document XML sans connatre XML ou les technologies d'analyse.
Toutes lesmanipulations se font au travers d'objets java. Ces
classes sont utilises pour faire correspondre le document XML dans
des instances de ces classes et vice et versa : ces oprationsse
nomment respectivement unmarshalling et marshalling.
JAXB 2.0
JAXB 2.0 a t dvelopp sous la JSR 222 et elle est incorpore dans
Java EE 5 et dans Java SE 6. Les fonctionnalits de JAXB 2.0 par
rapport JAXB 1.0 sont :
1. Support uniquement des schmas XML (les DTD ne sont plus
supportes).
2. Mise en oeuvre des annotations.
3. Assure la correspondance bidirectionelle entre un schma XML
et le bean correspondant.
4. Utilisation de fonctionnalits proposes par Java 5 notamment
les generiques et les numrations.
5. Le nombre d'entits gnres est moins important : JAXB 2.0 gnre
une classe pour chaque complexType du schema alors que JAXB 1.0
gnre une interface et une classe quiimplmente cette interface. Une
mthode de la classe ObjectFactory est gnre pour renvoye une
instance de cette classe.
En plus de son utilit principale, JAXB 2.0 propose d'atteindre
plusieurs objectifs :
1. tre facile utiliser pour consulter et modifier un document
XML sans connaissance ni de XML ni de techniques de traitement de
documents XML.
2. tre configurable : JAXB met en oeuvre des fonctionnalits par
dfaut qu'il est possible de modifier par configuration pour rpondre
ces propres besoins.
3. S'assurer que la cration d'un document XML partir d'objets et
retransformer ce document en objets donne le mme ensemble
d'objets.
4. Pouvoir valider un document XML ou les objets qui encapsulent
un document sans avoir crire le document correspondant.
5. tre portable : chaque implmentation doit au minimum mettre en
oeuvre les spcifications de JAXB.
L'utilisation de JAXB implique gnralement deux tapes :
1. Gnration des classes et interfaces partir du schma XML.
2. Utilisation des classes gnres et de l'API JAXB pour
transformer un document XML en graphe d'objets et vice et versa,
pour manipuler les donnes dans le graphe d'objets etpour valider le
document.
-
Srialisation
La srialisation d'un graphe d'objets Java est effectu par une
opration dite de mashalling. L'opration inverse est dite
d'unmashalling. Lors de ces deux oprations, le document XMLpeut tre
valid.
L'API JAXB
L'API JAXB propose un framework compos de classes regroupes dans
trois packages :
1. javax.xml.bind : Contient les interfaces principales et la
classe JAXBContext.
2. javax.xml.bind.util : Contient des utilitaires.
3. javax.xml.bind.helper : Contient une implmentation partielle
de certaines interfaces pour faciliter le dveloppement d'une
implmentation des spcifications de JAXB.
4. javax.xml.bin.annotation : Gestion des annotations pour
prciser le mapping entre les classes Java et le document XML
correspondant.
Annotations
JAXB 2.0 utilise de nombreuses annotations dfinies dans le
package javax.xml.bin.annotation essentiellement pour prciser le
mode de fonctionnement lors des oprations
demarshaling/unmarshaling.
Ces annotations prcisent le mapping entre les classes Java et le
document XML. La plupart de ces annotations ont des valeurs par
dfaut ce qui rduit l'obligation de leurutilisation si la valeur par
dfaut correspond au besoin.
Transformations
JAXB 2.0 permet aussi de raliser dynamiquement l'excution une
transformation d'un graphe d'objets en document XML et vice et
versa. C'est cette fonctionnalit qui est largementutilise dans les
services web via l'API JAXWS 2.0. La classe abstraite JAXBContext
fournie par l'API JAXB permet de grer la transformation d'objets
Java en XML et vice et versa.
Outils de JAXB 2.0
JAXB 2.0 propose plusieurs outils pour faciliter sa mise en
oeuvre :
1. Un gnrateur de classes Java (schema compiler) partir d'un
schma XML nomm xjc dans l'implmentation de rfrence. Ces classes
gnres mettent en oeuvre lesannotations de JAXB.
2. Un gnrateur de schma XML (schema generator) partir d'un
graphe d'objets nomm schemagen dans l'implmentation de rfrence.
L'API JAXB est contenue dans la package javax.xml.bind.
-
La mise en oeuvre de JAXB 2.0
La mise en oeuvre de JAXB requiert pour un usage standard
plusieurs tapes :
1. La gnration des classes en utilisant l'outil xjc de JAXB
partir d'un schma du document XML.
2. Ecriture de code utilisant les classes gnres et l'API JAXB
pour :Transformer un document XML en objets Java.
Modifier des donnes encapsules dans le graphe d'objets.
Transformer le graphe d'objets en un document XML avec une
validation optionnelle du document.
3. Compilation du code gnr.
4. Excution de l'application.
Utilisation de JAXB 2.0
L'utilisation de JAXB se fait donc en deux phases :
1. Gnrer des classes partir d'un schma XML et utiliser ces
classes dans le code de l'application.
2. l'excution de l'application, le document XML est transform en
graphe d'objets, les donnes de ces objets peuvent tre modifies puis
le document XML peut tre regnr partir des objets.
L'utilisation de JAXB met en oeuvre plusieurs lments :
1. Une ou plusieurs classes annotes qui vont encaspules des
donnes du document XML.
2. Un schma XML (optionnel) : c'est un document XML qui dcrit la
structure des lments, attributs et entits d'un document XML. Le but
d'un schma XML est similaire celuid'une DTD mais le schma propose
une description plus riche et plus fine. Ce schma peut
ventuellement tre enrichi de donnes de configurations concernant
les classes gnrer.
3. Un outil (optionnel) qui gnre les classes annotes partir d'un
schma avec ventuellement un fichier de configuration pour
configurer les classes gnrer.
4. Une API utilise l'excution pour transformer un document XML
en un ensemble d'objets du type des classes annotes et vice et
versa et permettre des validations.
5. Un document XML qui sera lu et/ou crit en fonction des
traitements raliser.
Avantages
Les avantages d'utiliser JAXB sont nombreux :
1. Facilite la manipulation de document XML dans une application
Java.
2. Manipulation du document XML au travers d'objets : aucune
connaissance de XML ou de la manire de traiter un document n'est
requise.
3. Un document XML peut tre cr en utilisant les classes
gnres.
4. Les traitements de JAXB peuvent tre configurs.
5. Les ressources requises par le graphe d'objets utilis par
JAXB sont moins importantes qu'avec DOM.
Inconvnients
Si vous devez crer des classes directement partir d'un document
XML, vous devez imprativement possder le Schma XML correspondant
(avec l'extension xsd). Si vous n'avez pas leSchma XML, cela peut
poser quelques difficults. C'est l'inconvnient de cette
technologie.
Toutefois, il existe des logiciels libres, comme Liquid XML
Studio, qui savent crer un Schma directement partir du document XML
correspondant. Ils proposent alors desvaleurs par dfaut que vous
pouvez par la suite modifier au travers des proprits proposes dans
l'diteur intgr.
-
Gnrer des classes partir d'un schma
Pour permettre l'utilisation et la manipulation d'un document
XML, JAXB propose de gnrer un ensemble de classes partir du schema
XML du document.
Chaque implmentation de JAXB doit fournir un outil (binding
compiler) qui permet la gnration de classes et interfaces partir
d'un schema (binding a schema).
Exemple de document XML avec son Schma XML associ
Je vous propose, pour la suite de cette tude, de prendre un
exemple concret, et surtout trs simple. J'aimerais pouvoir mettre
en oeuvre un logiciel qui trace des formes, des carrs oudes
cercles, dans la partie principale d'une application graphique
fentre.
Il doit tre possible de rcuprer l'ensemble des formes tracer au
moyen, par exemple, du document XML ci-dessous :
En utilisant le logiciel Liquid XML Studio, et en faisant les
rglages ncessaires, voici le Schma XML correspondant :
-
La commande xjc
Comme nous l'avons vu, l'implmentation de rfrence fournit
l'outil xjc pour gnrer les classes partir d'un schma XML.
L'utilisation la plus simple de l'outil xjc est de lui
fournirsimplement le fichier qui contient le schma XML du document
utiliser.
xjc Formes.xsd
L'outil xjc possde plusieurs options dont voici les principales
:
Option Rle
-p nom_package Prcise le package qui va contenir les classes
gnres
-d rpertoire Prcise le rpertoire qui va contenir les classes
gnres
-nv Inhibe la validation du schma
-b fichier Prcise un fichier de configuration
-classpath chemin Prcise le classpath
Les classes gnres
Le compilateur gnre des classes en correspondance avec le Schma
XML fourni l'outil.
-
Deux entits sont gnres dans le rpertoire generated :
1. Formes.java : classes qui encaspulent le document XML. Chaque
classse qui encapsule un type complexe du schma possde des getter
et setter sur les lments du schma.
2. ObjectFactory.java : fabrique qui permet d'instancier des
objets utiliss lors du mapping. La fabrique permet de crer des
instances de chacun des types d'objet correspondant un type
complexe du schma. Cette fabrique est particulirement utile lors de
la cration d'un nouveau document XML : le graphe d'objets est cr en
ajoutant des instances desobjets retournes par cette fabrique.
Les classes gnres sont dpendantes de l'implmentation de JAXB
utilise : il est prfrable d'utiliser les classes gnres par une
implmentation avec cet outil. Par dfaut,JAXB utilise des rgles pour
dfinir chaque entit incluse dans le schema (element et complexType
dfini dans le schma).
Formes.java (sans les commentaires)
package jaxb;
//----------------------------------------------------------------------------------------------------------
Ligne rajoute
import java.util.ArrayList;import java.util.List;import
javax.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
//------------------------------------------------------------
Element racine@XmlType(name = "", propOrder =
{"forme"})@XmlRootElement(name = "Formes")public class Formes {
@XmlElement(name = "Forme") protected List forme;
public List getForme() { if (forme == null) { forme = new
ArrayList(); } return this.forme; }
@XmlAccessorType(XmlAccessType.FIELD)
//----------------------------------------------------- Element
Forme @XmlType(name = "", propOrder = {"centre","largeur"}) public
static class Forme {
protected Formes.Forme.Centre centre;
@XmlSchemaType(name = "unsignedInt") protected Long largeur;
@XmlAttribute(required = true) protected String type;
public Formes.Forme.Centre getCentre() { return centre; }
public void setCentre(Formes.Forme.Centre value) { this.centre =
value; }
-
public void setCentre(Formes.Forme.Centre value) { this.centre =
value; }
public Long getLargeur() { return largeur; } public void
setLargeur(Long value) { this.largeur = value; }
public String getType() { return type; } public void
setType(String value) { this.type = value; }
@XmlAccessorType(XmlAccessType.FIELD)
//--------------------------------------------------- Element
Centre @XmlType(name = "") public static class Centre {
@XmlAttribute(required = true) protected int x;
@XmlAttribute(required = true) protected int y;
public int getX() { return x; } public void setX(int value) {
this.x = value; }
public int getY() { return y; } public void setY(int value) {
this.y = value; } } }}
ObjectFactory.java (sans les commentaires)
package jaxb;
//----------------------------------------------------------------------------------------------------------
Ligne rajoute
import javax.xml.bind.annotation.XmlRegistry;
@XmlRegistrypublic class ObjectFactory {
public ObjectFactory() {}
public Formes.Forme createFormesForme() { return new
Formes.Forme(); }
public Formes createFormes() { return new Formes(); }
public Formes.Forme.Centre createFormesFormeCentre() { return
new Formes.Forme.Centre(); }}
Utiliser l'API JAXB 2.0
JAXB fournie une API qui permet l'excution d'effectuer les
oprations de transformation d'un document XML en un graphe d'objets
utilisant les classes gnres et vice et
versa(unmashalling/marshalling) ainsi que des oprations de
validation.
1. L'objet principal pour les oprations de transformation est
l'objet JAXBContext : il permet d'utiliser l'API JAXB. Pour obtenir
une instance de cet objet, il suffit d'utiliser lamthode statique
newInstance() en lui passant en argument le ou les paquetages
contenant les classes gnres utiliser. Dans le cas ou plusieurs
paquetages doivent treprciss, il faut les sparer par une
virgule.
JAXBContext contexte = JAXBContext.newInstance("jaxb");
2. Une autre surdfinition de la mthode newInstance() attend en
argument la classe qui encapsule la racine du document.
Mapper un document XML des objets (unmarshal)
L'API JAXB propose de transformer un document XML en un ensemble
d'objets qui vont encapsuler les donnes et la hirarchie du
document. Ces objets sont des instances des classesgnres partir du
schma XML.
1. La cration des objets ncessite la cration d'un objet de type
JAXBContext en utilisant la mthode statique newInstance(), comme
nous venons de le dcouvrir.
2. 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 lamthode
createUnmarshaller() de la classe JAXBContext.
Unmarshaller mapperXMLObjet = contexte.createUnmarshaller();
3. La mthode 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. Ellepossde de nombreuses
surdfinitions utiliser en fonction des besoins.
Formes formes = (Formes) mapperXMLObjet.unmarshal(new
File("Formes.xml"));
4. A partir de cet objet, il est possible d'obtenir et de
modifier des donnes encapsules dans les diffrents objets crs partir
des classes gnres et du contenu du document.Chacun de ces objets
possdent des getter et des setter sur leur noeud direct.
Exemple de mise en oeuvre au travers de l'application fentre
Je vous propose de finaliser ces concepts au travers de
l'application fentre qui trace les formes stockes dans le document
XML, dont voici le rsultat :
-
Codage de l'application fentre
package jaxb;
import java.awt.*;import java.io.File;import
javax.swing.*;import javax.xml.bind.*;
public class Principal extends JFrame { private Trac dessins =
new Trac();
public Principal() { super("Trac de formes"); add(dessins);
setSize(400, 300); setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); }
public static void main(String[] args) { new Principal(); }
private class Trac extends JComponent { private Formes formes;
public Trac() { try { JAXBContext contexte =
JAXBContext.newInstance("jaxb"); Unmarshaller mapperXMLObjet =
contexte.createUnmarshaller(); formes = (Formes)
mapperXMLObjet.unmarshal(new File("Formes.xml")); } catch
(JAXBException ex) { setTitle("Impossible de rcuprer le document
XML"); } }
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g); Graphics2D surface = (Graphics2D) g;
surface.setStroke(new BasicStroke(5));
surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); if (formes!=null) for
(Formes.Forme forme : formes.getForme()) { long largeurLong =
forme.getLargeur(); int largeur = (int) largeurLong; int x =
forme.getCentre().getX() - largeur/2; int y =
forme.getCentre().getY() - largeur/2; String type =
forme.getType(); if (type.equals("Carr")) surface.drawRect(x, y,
largeur, largeur); else surface.drawOval(x, y, largeur, largeur); }
} }}
Crer un document XML partir d'objets
JAXB permet de crer un document XML partir d'un graphe d'objets
: cette opration est nomme marshalling. Une operation de mashalling
est l'opration inverse de l'oprationd'unmarshalling.
Ce graphe d'objets peut tre issu d'une opration de type
unmarshalling (construction partir d'un document XML existant) ou
issu d'une cration de toutes pices de l'ensembledes objets. Dans le
premier cas cela correspond une modification du document et dans le
second cas une cration de document.
1. La cration des objets ncessite la cration d'un objet de type
JAXBContext en utilisant la mthode statique newInstance(), comme
prcdemment.
2. Il faut ensuite instancier un objet de type Marshaller qui va
permettre de transformer un ensemble d'objets en un document XML.
Un telle instance est obtenue en utilisant lamthode
createMarshaller() de la classe JAXBContext.
3. La mthode marshal() de la classe Marshaller se charge de crer
un document XML partir d'un graphe d'objets dont l'objet racine lui
est fourni en paramtre. La mthodemarshal() possde plusieurs
surdfinition qui permettent de prciser la forme du document XML gnr
:
un fichier (File),
-
un flux (OutputStream),
un flux de caractre (Writer),
un document DOM (Document),
un gestionnaire d'vnement SAX (ContentHandler),
un objet de type javax.xml.transform.SAXResult,
un objet de type javax.xml.transform.DOMResult,
un objet de type javax.xml.transform.StreamResult,
un objet de type javax.xml.stream.XMLStreamWriter,
ou un objet de type javax.xml.stream.XMLEventWriter.
4. L'objet Mashaller possde des proprits qu'il est possible de
valoriser en utilisant la mthode setProperty(). Les spcifications
de JAXB proposent des proprits qui doivent treobligatoirement
supportes par l'implmentation. Chaque implmentation est libre de
proposer des proprits supplmentaires.
JAXB_ENCODING : Permet de prciser le jeux de caractres
d'encodage du document XML.
JAXB_FORMATTED_OUTPUT : Boolen qui indique si le document XML
doit tre format.
Exemple : demander le formattage du document cr :
Marshaller mapperObjetsXML =
contexte.createMarshaller();mapperObjetsXML.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
true);
La cration d'un document XML partir d'objets peut se faire
suivant deux approches qui seront traites dans les chapitres qui
suivent :
1. En utilisant les classes gnres partir d'un Schma XML.
2. En utilisant des classes annotes sans tre oblig d'utiliser un
Schma XML.
Cration d'un document XML partir d'objets suivant un Schma
XML
Comme nous l'avons dcouvert dans les chapitres prcdents, une des
classes gnres partir du schma se nomme ObjectFactory : c'est une
fabrique d'objets pour les classes gnresqui encapsulent des donnes
d'un document respectant le schma.
Pour crer un document XML en utilisant ces classes, il faut
suivre les tapes que nous avons voqu dans la fin du chapitre
prcdent :
1. La cration du document ncessite la cration d'un objet de type
JAXBContext en utilisant la mthode statique newInstance().
2. Il faut ensuite crer le graphe d'objets en utilisant la
classe ObjectFactory pour instancier les diffrents objets et
valoriser les donnes de ces objets en utilisant les setter.
ObjectFactory fabrique = new ObjectFactory(); Formes formes =
fabrique.createFormes();Formes.Forme forme =
fabrique.createFormesForme();Formes.Forme.Centre centre =
fabrique.createFormesFormeCentre();
3. Il faut ensuite crer un objet de type Marshaller partir du
contexte et appeler sa mthode marshall() pour gnrer le
document.
Exemple de mise en oeuvre au travers de l'application fentre
Je vous propose de finaliser ces concepts au travers d'une
application fentre qui trace les formes directement sur la zone
principale de la fentre et qui peut stocker par la suite tous
lesformes prsentes dans le document XML correspondant :
Codage de l'application fentre
package jaxb;
import java.awt.*;import java.awt.event.*;import
java.io.File;import javax.swing.*;import javax.xml.bind.*;
public class Principal extends JFrame { private Trac dessins =
new Trac(); private JRadioButton cercle = new
JRadioButton("Cercle", true); private JRadioButton carr = new
JRadioButton("Carr"); private JFormattedTextField largeur = new
JFormattedTextField(50L); private ButtonGroup groupe = new
ButtonGroup(); private JPanel boutons = new JPanel(); private
JToolBar barre = new JToolBar();
-
public Principal() { super("Trac de formes"); add(barre,
BorderLayout.NORTH); barre.add(new AbstractAction("Nouveau", new
ImageIcon("nouveau.gif")) { public void actionPerformed(ActionEvent
e) { dessins.effacer(); } }); barre.add(new
AbstractAction("Enregistrer", new ImageIcon("enregistrer.gif")) {
public void actionPerformed(ActionEvent e) { dessins.enregistrer();
} }); dessins.addMouseListener(new MouseAdapter() { @Override
public void mouseClicked(MouseEvent e) { if (cercle.isSelected())
dessins.ajoutForme("Cercle", e.getX(), e.getY()); else
dessins.ajoutForme("Carr", e.getX(), e.getY()); } }); add(dessins);
largeur.setColumns(5); boutons.add(cercle); boutons.add(carr);
boutons.add(largeur); groupe.add(cercle); groupe.add(carr);
add(boutons, BorderLayout.SOUTH); setSize(400, 300);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); }
public static void main(String[] args) { new Principal(); }
private class Trac extends JComponent { private Formes formes;
private Marshaller mapperObjetsXML; private ObjectFactory fabrique
= new ObjectFactory(); public Trac() { try { JAXBContext contexte =
JAXBContext.newInstance(Formes.class); mapperObjetsXML =
contexte.createMarshaller();
mapperObjetsXML.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
true); formes = fabrique.createFormes(); } catch (JAXBException ex)
{ setTitle("Impossible de crer le document XML"); } }
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g); Graphics2D surface = (Graphics2D) g;
surface.setStroke(new BasicStroke(5));
surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); if (formes!=null) for
(Formes.Forme forme : formes.getForme()) { long largeurLong =
forme.getLargeur(); int largeur = (int) largeurLong; int x =
forme.getCentre().getX() - largeur/2; int y =
forme.getCentre().getY() - largeur/2; String type =
forme.getType(); if (type.equals("Carr")) surface.drawRect(x, y,
largeur, largeur); else surface.drawOval(x, y, largeur, largeur); }
}
public void effacer() { formes = fabrique.createFormes();
revalidate(); repaint(); }
public void ajoutForme(String type, int x, int y) { Formes.Forme
forme = fabrique.createFormesForme(); forme.setType(type);
forme.setLargeur((Long) largeur.getValue()); Formes.Forme.Centre
centre = fabrique.createFormesFormeCentre(); centre.setX(x);
centre.setY(y); forme.setCentre(centre);
formes.getForme().add(forme); repaint(); }
private void enregistrer() { try {
mapperObjetsXML.marshal(formes, new File("Formes.xml")); } catch
(JAXBException ex) { setTitle("Impossible d'enregistrer le document
XML"); } } }
}
Mapping XML/Objets en passant par les annotations
JAXB permet de mapper un document XML vers une ou plusieurs
classes annotes, ou inversement, sans tre oblig d'utiliser un schma
XML. Dans ce cas, le dveloppeur a la charge
-
d'crire la ou les classes annotes requises.
Dans nos dveloppement, il arrive trs souvent que nous
manipulions un ensemble d'informations, de diffrentes natures,
comme un systme de gestion de personnel ou encorecomme le logiciel
de trac de formes. Ces informations sont toujours structures sous
forme de classes, et derrire, nous manipulons les objets
correspondants.
Nous avons quelquefois besoin de pouvoir stocker ces
informations dans un fichier afin de les retrouver par la suite. Se
pose alors la question du format de stockage.Nous avons vu que le
document XML est une bonne alternative puisqu'il est facile de le
lire avec un simple diteur de texte.
Dans ce contexte, il serait judicieux de pouvoir fabriquer ces
documents XML directement par l'intermdiaire des classes dj
existantes (mapping Objets/XML), sanspasser par un Schma XML
(puisqu'encore aucun document n'existe). C'est dans ce cadre l que
nous pouvons rajouter des annotations spcifiques directement sur
lesclasses concernes.
Il n'est donc plus ncessaire d'utiliser des classes gnres par le
compilateur de schma mais dans ce cas, la ou les classes doivent
tre cres manuellement en utilisant lesannotations adquates. La
classe qui encapsule la racine du document doit tre annote avec
l'annotation @XmlRootElement. Une exception de
typejavax.xml.bind.MarshalException est leve par JAXB si la classe
racine ne possde pas cette annotation.
JAXB utilise des comportements par dfaut qui ne ncessite de la
part du dveloppeur que de dfinir des comportements particuliers si
la valeur par dfaut ne convient pas. Ainsi,nous pouvons limiter le
nombre d'annotations requises.
Communication entre le document XML et les objets
correspondants
Comme nous l'avons dcouvert dans les chapitres prcdents, le
mapping entre un document XML et les objets correspondants, se
feront par l'intermdiaires des classes JAXBContext,Marshaller et
Unmarshaller.
1. La cration ou la connexion avec un document XML ncessite la
cration d'un objet de type JAXBContext en utilisant la mthode
statique newInstance().
2. Il faut ensuite crer un objet de type Marshaller partir du
contexte et d'appeler sa mthode marshall() pour gnrer le document
XML partir des objets dj crs.
3. Ou bien crer un objet de type Unmarshaller partir du contexte
et d'appeler sa mthode unmarshall() pour gnrer les objets
correspondant au document XML.
Annotation des classes
La configuration de la transformation d'un document XML en
objets Java est ralise grce l'utilisation d'annotations ddies dans
les classes Java. De nombreuses annotations sontdfinies par JAXB
2.0. Toutes ces annotations sont dfinies dans le package
javax.xml.bind.annotation.
Annotation Description
XmlAccessorOrder Contrler l'ordre des attributs et des proprits
dans la classe.
XmlAccessorType Contrler si l'attribut ou la proprit de la
classe est srialis par dfaut.
XmlAnyAttribute Convertir une proprit de la classe en un
ensemble d'attributs de type jocker dans le document XML.
XmlAnyElement Convertir une proprit de la classe en une
description representant l'lment JAXB dans le document XML.
XmlAttachmentRef Marquer un attribut ou une proprit de la classe
comme une URI que fait rfrence une type MIME particulier.
XmlAttribute Convertir une proprit de la classe en un attribut
dans le document XML.
XmlElement Convertir une proprit de la classe en un lment dans
le document XML.
XmlElementDecl Associer une fabrique un element XML.
XmlElementRef Convertir une proprit de la classe en un lment
dans le document XML qui est associ un nom de proprit
particulier.
XmlElementRefs Marquer une proprit de la classe qui fait rfrence
aux classes qui possdente XmlElement ou JAXBElement.
XmlElements Crer un conteneur pour de multiples annotations
XmlElement, qui prcise ainsi le choix possible parmi celles qui
sont proposes.
XmlElementWrapper Crer un lment pre dans le document XML pour
des collections d'lments.
XmlEnum Convertir une numration en une reprsentation quivalente
dans le document XML.
XmlEnumValue Convertir un numrateur en une reprsentation
quivalente dans le document XML.
XmlID Convertir une proprit de la classe en un ID XML.
XmlIDREF Convertir une proprit de la classe en un IDREF XML.
XmlInlineBinaryData Encoder en base64 dans le document XML.
XmlList Utilis pour convertir une proprit de la classe vers une
liste de type simple.
XmlMimeType Associer un type MIME qui contrle la reprsentation
XML en rapport avec la proprit de la classe.
XmlMixed Annoter une proprit de la classe qui peut supporter
plusieurs types de valeur avec un contenu mixte.
XmlNs Associer un prefix d'un espace de nommage une URI.
XmlRegistry Marquer une classe comme possdant une ou des mthodes
annotes avec XmlElementDecl.
XmlRootElement Associer une classe ou une numration un element
XML. Trs souvent utilis pour spcifier la racine du document
XML.
XmlSchema Associer un espace de nommage un paquetage.
XmlSchemaType Associer un type Java ou une numeration un type
dfini dans un schma.
XmlSchemaTypes Conteneur pour plusieurs proprits annotes par
XmlSchemaType.
XmlTransient Marquer une entit pour qu'elle ne soit pas mappe
dans le document XML.
XmlType Convertir une classe ou une numration vers le type
spcifi dans Shma XML correspondant.
XmlValue Mapper une classe vers le type complexe dans le Schma
XML ou vers le type simple suivant le cas.
-
Premier exemple de mise en oeuvre - pas d'attributs dans les
balises
Nous allons toute de suite laborer un premier exemple qui permet
de faire du trac de formes, comme prcdemment, avec la possiblit de
sauvegarder le trac ralis sur l'IHM dans undocument XML et de
pouvoir ensuite le restituer loisir. Tout ceci se fait sans
document XML pralable, et plus forte raison, sans Schma XML
quivalent.
Dans ce premier exemple, nous ne proprosons pas d'attributs dans
les balises. Du coup, il devient ncessaire de prvoir des balises
spcifiques pour les coordonnes. Remarquezau passage que notre
document Formes, reprsent par l'lment racine peut aussi bien
accueillir des lments que des lments , ce qui offre unestructure un
petit peu plus complique que prcdemment.
Mise en place du source relatif aux formes
package jaxb;
import java.awt.Graphics2D;import java.util.ArrayList;import
javax.xml.bind.annotation.*;
// Dfinition de l'lment racine du document et de ses
sous-lments@XmlRootElementpublic class Formes { @XmlElements({
@XmlElement(name = "carr", type = Carr.class), @XmlElement(name =
"cercle", type = Cercle.class) }) private ArrayList formes = new
ArrayList();
public ArrayList getFormes() { return formes; } public void
ajoutForme(Forme forme) { formes.add(forme); } public void
supprimerFormes() { formes.clear(); }}
// hirarchie des classes reprsentant l'ensemble des formes
possiblesabstract class Forme { protected int x, y; public Forme()
{} public Forme(int x, int y) { this.x = x; this.y=y; } public int
getX() { return x; } public void setX(int x) { this.x = x; } public
int getY() { return y; } public void setY(int y) { this.y = y; }
abstract void dessine(Graphics2D surface);}
class Cercle extends Forme { private int rayon = 50; public
Cercle() {} public Cercle(int x, int y, int rayon) { super(x, y);
this.rayon = rayon; } public int getRayon() { return rayon; }
public void setRayon(int rayon) { this.rayon = rayon; } @Override
void dessine(Graphics2D surface) { surface.drawOval(x-rayon,
y-rayon, 2*rayon, 2*rayon); }}
class Carr extends Forme { private int ct = 100; public Carr()
{} public Carr(int x, int y, int ct) { super(x, y); this.ct = ct; }
public int getCt() { return ct; } public void setCt(int ct) {
this.ct = ct; } @Override void dessine(Graphics2D surface) {
surface.drawRect(x-ct/2, y-ct/2, ct, ct); }}
Dans ce source, nous dcouvrons deux parties essentielles :
1. La hirarchie des classes qui reprsente l'ensemble des formes,
avec mme une classe de base abstraite. Ce sont ces classes que nous
utilisons directement dans l'IHM.Remarquez, qu'elles ne possdent
aucune annotation particulire.
2. La premire partie du code est cette fois-ci plus spcifique
l'laboration du document XML. Dans ce cadre l, une classe Formes
est spcialement cre pour reprsenterl'lment racine du document qui
est spcifi au travers de l'annotation @XmlRootElement.
3. Cette classe Formes possde un seul attribut : formes qui
reprsente l'ensemble des formes susseptibles d'tre enregistres.
Comme il existe plusieurs formes possibles, nousutilisons une
annotation particulire, juste au-dessus de l'attribut, qui va
servir prciser quels sont les lments autoriss intgrer l'ensemble
des formes. Il s'agit del'annotation @XmlElements l'intrieur de
laquelle nous allons positionner des annotations de type
@XmlElement qui vont permettre de choisir prcisment la classe
prendreen compte avec le nom de la balise associe. Ainsi, lorsque
nous placerons une forme, le systme proposera la balise
correspondante parmi celles qui font partie de l'ensembledfini par
l'annotation @XmlElements.
L'attribut name de l'annotation @XmlElement permet d'indiquer le
nom de la balise dans le document XML.
L'attribut type de l'annotation @XmlElement permet d'indiquer le
nom de la classe associe.
4. Et c'est tout ... Finalement, nous utilisons trs trs peu
d'annotations.
-
Codage de l'application principale
package jaxb;
import java.awt.*;import java.awt.event.*;import
java.io.File;import javax.swing.*;import javax.xml.bind.*;import
jaxb.Formes.*;
public class Principal extends JFrame { private Trac dessins =
new Trac(); private JRadioButton cercle = new
JRadioButton("Cercle", true); private JRadioButton carr = new
JRadioButton("Carr"); private JFormattedTextField largeur = new
JFormattedTextField(50); private ButtonGroup groupe = new
ButtonGroup(); private JPanel boutons = new JPanel(); private
JToolBar barre = new JToolBar();
public Principal() { super("Trac de formes"); add(barre,
BorderLayout.NORTH); barre.add(new AbstractAction("Nouveau", new
ImageIcon("nouveau.gif")) { public void actionPerformed(ActionEvent
e) { dessins.effacer(); } }); barre.add(new
AbstractAction("Ouvrir", new ImageIcon("ouvrir.gif")) { public void
actionPerformed(ActionEvent e) { dessins.ouvrir(); } });
barre.add(new AbstractAction("Enregistrer", new
ImageIcon("enregistrer.gif")) { public void
actionPerformed(ActionEvent e) { dessins.enregistrer(); } });
dessins.addMouseListener(new MouseAdapter() { @Override public void
mouseClicked(MouseEvent e) { int larg =
(Integer)largeur.getValue(); if (cercle.isSelected())
dessins.ajoutForme(new Cercle(e.getX(), e.getY(), larg/2)); else
dessins.ajoutForme(new Carr(e.getX(), e.getY(), larg)); } });
add(dessins); largeur.setColumns(3); boutons.add(cercle);
boutons.add(carr); boutons.add(largeur); groupe.add(cercle);
groupe.add(carr); add(boutons, BorderLayout.SOUTH); setSize(400,
300); setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); }
public static void main(String[] args) { new Principal(); }
private class Trac extends JComponent { private Formes formes =
new Formes(); private Marshaller mapperObjetsXML; private
Unmarshaller mapperXMLObjets; public Trac() { try { JAXBContext
contexte = JAXBContext.newInstance(Formes.class); mapperObjetsXML =
contexte.createMarshaller();
mapperObjetsXML.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
true); mapperXMLObjets = contexte.createUnmarshaller(); } catch
(JAXBException ex) { setTitle("Impossible de crer le document
XML"); } }
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g); Graphics2D surface = (Graphics2D) g;
surface.setStroke(new BasicStroke(5));
surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); if (formes!=null) for (Forme
forme : formes.getFormes()) forme.dessine(surface); }
public void effacer() { formes.supprimerFormes(); revalidate();
repaint(); }
public void ajoutForme(Forme forme) { formes.ajoutForme(forme);
repaint(); }
private void enregistrer() { try {
mapperObjetsXML.marshal(formes, new File("Formes.xml")); } catch
(JAXBException ex) { setTitle("Impossible d'enregistrer le document
XML"); } }
-
private void ouvrir() { try { formes = (Formes)
mapperXMLObjets.unmarshal(new File("Formes.xml")); repaint(); }
catch (JAXBException ex) { setTitle("Impossible d'enregistrer le
document XML"); } } }}
Vous remarquez ici, par rapport aux chapitres prcdents, que la
gestion des formes est beaucoup plus simple, puisqu'il s'agit de
classes des plus normales sans annotationsparticulires. Le fait
d'avoir d'ailleurs une classe abstraite commune permet de
simplifier encore plus le problme.
L'utilisation des annotations sans passer par une Schma XML me
parat finalement plus facile mettre en oeuvre et surtout plus
intuitif. L'intrt du Schma XML est surtoutintressant lorsque nous
devons faire une application qui tient compte de documents XML dj
existants.
Quelques considrations techniques sur certaines annotations
Le tableau ci-dessous reprend quelques-unes des annotations pour
bien prciser leur particularit et leur utilit.
Annotation Considrations
XmlRootElement Peut tre utilise sur une classe pour prciser que
cette classe sera l'lment racine du document XML. Chaque attribut
de la classe devient ainsi unlment fils dans le document XML.
L'attribut namespace de l'annotation @XmlRootElement permet de
prciser l'espace de nommage.
Codage de la classe Personne qui va servir de rfrence
import java.util.Date; import javax.xml.bind.annotation.*;
@XmlRootElement public class Personne { private String nom; private
String prenom; private int taille; private Date dateNaissance;
public Personne() { } public Personne(String nom, String prenom,
int taille, Date dateNaissance) { super(); this.nom = nom;
this.prenom = prenom; this.taille = taille; this.dateNaissance =
dateNaissance; } public Date getDateNaissance() { return
dateNaissance; } public void setDateNaissance(Date dateNaissance) {
this.dateNaissance = dateNaissance; } 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; } }
Rsultat
REMY Emmanuel 171 2007-01-16T17:03:31.213+01:00
XmlTransient Permet d'ignorer une proprit/entit dans la mapping
entre l'objet et l'lment XML correspondant :
Exemple
import java.util.Date; import javax.xml.bind.annotation.*;
@XmlRootElement public class Personne { ... @XmlTransient
-
public String getNom() { return nom; } ...
}
Rsultat
Emmanuel 171 2007-01-16T17:03:31.213+01:00
XmlAttribute Permet de mapper une proprit sous la forme d'un
attribut et fournir des prcisions sur la configuration de cet
attribut
Exemple
import java.util.Date; import javax.xml.bind.annotation.*;
@XmlRootElement public class Personne { ... @XmlAttribute public
String getNom() { return nom; } ...}
Rsultat
Emmanuel 171 2007-01-16T17:03:31.213+01:00
XmlElementWrapper Pour les collections, il est possible
d'utiliser l'annotation @XmlElementWrapper pour dfinir un lement
pre qui encapsule les occurrences de lacollection et d'utiliser
l'annotation @XmlElement pour prciser le nom de chaque lment de la
collection.
Exemple
import java.util.Date; import javax.xml.bind.annotation.*;
@XmlRootElement public class Personne { private String nom; private
String prenom; private int taille; private Date dateNaissance;
@XmlElementWrapper(name = "Residence") @XmlElement(name =
"Adresse") protected List adresses = new ArrayList(); ... }
class Adresse { private String rue; private int codePostal;
private String ville; private String pays;...}
Rsultat
REMY Emmanuel 171 2007-01-16T17:03:31.213+01:00 Nom de la rue
78895 Une ville France
XmlType Il est possible de configurer l'ordre des lments au
travers de cette annotation
Exemple
import java.util.Date; import javax.xml.bind.annotation.*;
@XmlRootElement @XmlType(propOrder = {"dateNaissance", "adresses",
"nom", "prenom", "taille"}) public class Personne { private String
nom; private String prenom; private int taille; private Date
dateNaissance;
@XmlElementWrapper(name = "Residence") @XmlElement(name =
"Adresse")
-
protected List adresses = new ArrayList(); ... }
class Adresse { private String rue; private int codePostal;
private String ville; private String pays;...
}
Rsultat
2007-01-16T17:03:31.213+01:00 Nom de la rue 78895 Une ville
France REMY Emmanuel 171
XmlJavaTypeAdapter Il est possible de dfinir des classes de type
Adapter qui permettent de personnaliser la faon dont un objet est
serialis/desrialis dans le documentXML. Ces classes Adapter hritent
de la classe javax.xml.bind.annotation.adapters.XmlAdapter. Il
suffit de redfinir les mthodes marshal() et unmashal()afin de
permettre un formatage de l'information adapt au visuel que nous
dsirons faire apparatre ct document XML. Ces mthodes
serontautomatiquement utilises l'excution par l'API JAXB lors des
transformations. L'annotation @XmlJavaTypeAdapter permet de prciser
la classe de typeAdapter qui sera utiliser plutt que d'utiliser la
conversion par dfaut
Exemple
import java.util.Date; import java.text.*; import
javax.xml.bind.annotation.*;import
javax.xml.bind.annotation.adapters.*; @XmlRootElement
@XmlType(propOrder = {"dateNaissance", "adresses", "nom", "prenom",
"taille"}) public class Personne { private String nom; private
String prenom; private int taille;
@XmlJavaTypeAdapter(DateAdapter.class) private Date dateNaissance;
@XmlElementWrapper(name = "Residence") @XmlElement(name =
"Adresse") protected List adresses = new ArrayList(); ... }
class Adresse { private String rue; private int codePostal;
private String ville; private String pays;...}
class DateAdapter extends XmlAdapter { private DateFormat
miseEnForme = DateFormat.getDateInstance(DateFormat.LONG);
public Date unmarshal(String date) throws Exception { return
miseEnForme.parse(date); }
public String marshal(Date date) throws Exception { return
miseEnForme.format(date); } }
Rsultat
1 octobre 1959 Nom de la rue 78895 Une ville France REMY
Emmanuel 171
Deuxime exemple de mise en oeuvre - Mise en place d'attributs
dans les balises XML
Nous allons reprendre l'application prcdente qui permet de
sauvegarder, et donc de restituer un ensemble de formes, dans un
document XML. Ce document XML est lgrementdiffrent que le prcdent.
Pour chacune des formes, nous envisageons, cette fois-ci, de
prendre une nouvelle balise nomme l'intrieur de laquelle nous
stipulons lescoordonnes du centre de la forme.
Cette fois-ci nous devons proposer des attributs, respectivement
x et y, qui vont reprsenter les coordonnes dela balise .
-
Mise en place du source relatif aux formes
package jaxb;
import java.awt.Graphics2D;import java.util.ArrayList;import
javax.xml.bind.annotation.*;
// Dfinition de l'lment racine du document et de ses
sous-lments@XmlRootElementpublic class Formes { @XmlElements({
@XmlElement(name = "carr", type = Carr.class), @XmlElement(name =
"cercle", type = Cercle.class) }) private ArrayList formes = new
ArrayList();
public ArrayList getFormes() { return formes; } public void
ajoutForme(Forme forme) { formes.add(forme); } public void
supprimerFormes() { formes.clear(); }}
// La classe Forme est modifie pour accueillir un centre la
place des coordonnes x et yabstract class Forme { protected Centre
centre; public Forme() { centre = new Centre(); } public Forme(int
x, int y) { centre = new Centre(x, y); } public Centre getCentre()
{ return centre; } public void setCentre(Centre centre) {
this.centre = centre; } abstract void dessine(Graphics2D
surface);}
// Nouvel lment reprsenant le qui possde deux attributs x et
yclass Centre { @XmlAttribute private int y; @XmlAttribute private
int x;
public Centre() {} public Centre(int x, int y) { this.x = x;
this.y = y; } public int getX() { return x; } public int getY() {
return y; } }
class Cercle extends Forme { private int rayon = 50; public
Cercle() {} public Cercle(int x, int y, int rayon) { super(x, y);
this.rayon = rayon; } public int getRayon() { return rayon; }
public void setRayon(int rayon) { this.rayon = rayon; } @Override
void dessine(Graphics2D surface) { int x = centre.getX(); int y =
centre.getY(); surface.drawOval(x-rayon, y-rayon, 2*rayon,
2*rayon); }}
class Carr extends Forme { private int ct = 100; public Carr()
{} public Carr(int x, int y, int ct) { super(x, y); this.ct = ct; }
public int getCt() { return ct; } public void setCt(int ct) {
this.ct = ct; } @Override void dessine(Graphics2D surface) { int x
= centre.getX(); int y = centre.getY(); surface.drawRect(x-ct/2,
y-ct/2, ct, ct); }}
Dans ce source, nous pouvons faire un certain nombre de
remarques :
1. La plus grosse partie du code n'est pas modifie.
2. Nous devons mettre en oeuvre une classe Centre qui, ds lors,
possde les coordonnes des formes souhaites. Les coordonnes
deviennent alors des attributs XML de cetteclasse Centre.
3. Dans la classe Forme, nous proposons un nouvel attribut
centre, de type Centre, qui remplace les attributs x et y. Les
constructeurs doivent prendre en compte la cration de cesdiffrents
centres.
4. Il faut revoir galement les mthodes redfinies dessine()
puisque nous ne pouvons plus accder directement aux coordonnes.
-
5. Encore une fois, malgrs un changement notable sur le document
XML, nous utilisons trs peu d'annotations.
Rpertoire tlphonique avec une MAP
Cette fois-ci, nous changeons de sujet. Nous proposons une
application qui permet de stocker un petit rpertoire tlphonique de
portable associ juste quelques prnoms connus. Cerpertoire devra
toutefois tre dans l'ordre alphabtique des prnoms.
Source du rpertoire tlphonique
package carte;
import java.awt.*;import java.awt.event.ActionEvent;import
java.io.File;import java.text.ParseException;import
javax.swing.*;import java.util.*;import
javax.swing.text.MaskFormatter;import javax.xml.bind.*;import
javax.xml.bind.annotation.*;
public class GestionRpertoire extends JFrame { private
JTextField prnom = new JTextField("Nouveau prnom", 18); private
JFormattedTextField tlphone; private JPanel panneau = new JPanel();
private JToolBar barre = new JToolBar(); private Marshaller
mapperObjetsXML; private Unmarshaller mapperXMLObjets; private
Rpertoire rpertoire = new Rpertoire(); private JComboBox liste =
new JComboBox();
public GestionRpertoire() throws ParseException {
super("Rpertoire tlphonique"); documentXML();
liste.setForeground(Color.RED); tlphone = new
JFormattedTextField(new MaskFormatter("##-##-##-##-##"));
tlphone.setValue("06-00-00-00-00"); barre.add(new
AbstractAction("Ajout") { public void actionPerformed(ActionEvent
e) { rpertoire.ajout(prnom.getText(), (String) tlphone.getValue());
rpertoire.miseAJour(liste); } }); barre.add(new
AbstractAction("Suppression") { public void
actionPerformed(ActionEvent e) {
rpertoire.suppression(prnom.getText()); rpertoire.miseAJour(liste);
} }); barre.add(new AbstractAction("Ouvrir", new
ImageIcon("ouvrir.gif")) { public void actionPerformed(ActionEvent
e) { try { rpertoire = (Rpertoire) mapperXMLObjets.unmarshal(new
File("Tlphones.xml")); rpertoire.miseAJour(liste); } catch
(JAXBException ex) { setTitle("Impossible d'enregistrer le document
XML"); } } }); barre.add(new AbstractAction("Enregistrer", new
ImageIcon("enregistrer.gif")) { public void
actionPerformed(ActionEvent e) { try {
mapperObjetsXML.marshal(rpertoire, new File("Tlphones.xml")); }
catch (JAXBException ex) { setTitle("Impossible d'enregistrer le
document XML"); } } }); add(barre, BorderLayout.NORTH);
panneau.add(new JLabel("Prnom : ")); panneau.add(prnom);
panneau.add(new JLabel("Tlphone : ")); panneau.add(tlphone);
add(panneau); add(liste, BorderLayout.SOUTH); pack();
setLocationRelativeTo(null); setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); }
private void documentXML() { try { JAXBContext contexte =
JAXBContext.newInstance(Rpertoire.class); mapperObjetsXML =
contexte.createMarshaller();
-
mapperObjetsXML.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
true); mapperXMLObjets = contexte.createUnmarshaller(); } catch
(JAXBException ex) { setTitle("Impossible de crer le document
XML"); } } public static void main(String[] args) throws
ParseException { new GestionRpertoire(); }}
@XmlRootElementclass Rpertoire { private TreeMap rpertoire = new
TreeMap();
public TreeMap getRpertoire() { return rpertoire; } public void
setRpertoire(TreeMap rpertoire) { this.rpertoire = rpertoire; }
public void ajout(String prnom, String numro) {
rpertoire.put(prnom, numro); } public void suppression(String
prnom) { rpertoire.remove(prnom); } public void miseAJour(JComboBox
liste) { liste.removeAllItems(); for (Map.Entry lment :
rpertoire.entrySet()) liste.addItem(lment); }}
Faisons un certain nombre de remarques :
1. La classe utilise pour stocker l'ensemble des numros est la
classe TreeMap qui permet de grer dans l'ordre ce que nous appelons
des cartes.
2. Nous plaons juste une annotation @XmlRootElement qui permet
de grer l'ensemble des numros au travers de ce Map.
3. Tout ce fait automatiquement.
4. L'inconvnient toutefois, c'est que le nom des balises, dans
le document XML, est alors impos. Nous trouvons donc des intituls
correspondant la terminologie utilise parl'interface Map,
c'est--dire : , et .
5. Il sera ventuellement ncessaire de passer par une classe de
type Adapter pour contrler le nom des balises du document XML.
Rpertoire tlphonique avec une MAP et une classe d'adaptation
pour le document XML
Nous reprenons le mme sujet que prcdemment. L'objectif ici est
de proposer une meilleure mise en forme du document XML pour une
lecture plus adapte la situation.
Partie du code sur la mise en forme du document XML
uniquement
...
@XmlRootElementclass Rpertoire { @XmlElement(name="tlphones")
@XmlJavaTypeAdapter(MapAdapter.class) private TreeMap rpertoire =
new TreeMap();
public TreeMap getRpertoire() { return rpertoire; } public void
ajout(String prnom, String numro) { rpertoire.put(prnom, numro); }
public void suppression(String prnom) { rpertoire.remove(prnom); }
public void miseAJour(JComboBox liste) { liste.removeAllItems();
for (Map.Entry lment : rpertoire.entrySet()) liste.addItem(lment);
}}
class MapAdapter extends XmlAdapter { @Override public TreeMap
unmarshal(Tlphones tlphones) throws Exception { TreeMap cartes =
new TreeMap(); for (Tlphone tlphone : tlphones.getTlphones())
cartes.put(tlphone.getPrnom(), tlphone.getNumro()); return cartes;
} @Override public Tlphones marshal(TreeMap valeurs) throws
Exception { Tlphones tlphones = new Tlphones(); for (Map.Entry
carte : valeurs.entrySet()) tlphones.getTlphones().add(new
Tlphone(carte.getKey(), carte.getValue())); return tlphones; }}
class Tlphones { @XmlElement(name="tlphone") private ArrayList
tlphones = new ArrayList(); public ArrayList getTlphones() { return
tlphones; }}
class Tlphone { @XmlAttribute private String numro;
@XmlAttribute private String prnom;
-
public String getNumro() { return numro; } public String
getPrnom() { return prnom; }
public Tlphone(String prnom, String numro) { this.prnom = prnom;
this.numro = numro; }
public Tlphone() {}}
Faisons un certain nombre de remarques :
1. Il n'existe pas d'annotation spcifiques un conteneur de type
Map. Nous devons donc tout construire pour avoir un document XML
suivant notre souhait.
2. Dans notre document XML, pour nommer un lment , nous devons
utiliser spcifiquement l'annotation @XmlElement. Or, cette
annotation ne se place qu'audessus d'une proprit de classe. La
seule solution consiste donc crer une classe conteneur qui possde
alors une liste d'lments. Ainsi chaque lment sera bien nomm, si le
choix de l'attribut de la classe porte le mme nom o si nous
spcifions le nom au travers de l'attribut name de l'annotation
@XmlElement.
3. Pour avoir des attributs sur cette balise , nous devons crer
galement une classe dont chacune des proprits aura comme annotation
@XmlAttribute. Ici aussi, lesattributs d'une balise correspondent
toujours aux attributs d'une classe. La cration de cette classe
devient donc obligatoire. Finalement la liste prcdente sera une
liste de cetteclasse.
4. Pour finir, il faut faire le lien entre la Map (liaison avec
le programme principal) et la liste (liaison avec le document UML).
La seule solution est de proposer une classe Adapterqui fait le
marshalling et l'unmarshalling de faon adquat.
Rpertoire tlphonique pouvant intgrer plusieurs tlphones
Nous reprenons de nouveau la mme application. Chaque utilisateur
peut cette fois-ci avoir plusieurs tlphones : le mobile, le tlphone
du domicile ou le tlphone du travail.
Source complet
package carte;
import java.awt.*;import java.awt.event.ActionEvent;import
java.io.File;import java.text.ParseException;import
javax.swing.*;import java.util.*;import
javax.swing.text.MaskFormatter;import javax.xml.bind.*;import
javax.xml.bind.annotation.*;import
javax.xml.bind.annotation.adapters.*;
public class GestionRpertoire extends JFrame { private
JTextField prnom = new JTextField("Nouveau prnom", 18); private
JFormattedTextField tlphone; private JPanel panneau = new JPanel();
private JToolBar barre = new JToolBar(); private Marshaller
mapperObjetsXML; private Unmarshaller mapperXMLObjets; private
Rpertoire rpertoire = new Rpertoire(); private JComboBox
typeTlphone = new JComboBox(TypeTlphone.values()); private
JComboBox liste = new JComboBox();
public GestionRpertoire() throws ParseException {
super("Rpertoire tlphonique"); documentXML();
liste.setForeground(Color.RED); tlphone = new
JFormattedTextField(new MaskFormatter("##-##-##-##-##"));
tlphone.setValue("06-00-00-00-00"); barre.add(new
AbstractAction("Ajout") { public void actionPerformed(ActionEvent
e) { rpertoire.ajout(prnom.getText(),
typeTlphone.getSelectedItem(), (String) tlphone.getValue());
rpertoire.miseAJour(liste); } }); barre.add(new
AbstractAction("Suppression") { public void
actionPerformed(ActionEvent e) {
rpertoire.suppression(prnom.getText()); rpertoire.miseAJour(liste);
} }); barre.add(new AbstractAction("Ouvrir", new
ImageIcon("ouvrir.gif")) { public void actionPerformed(ActionEvent
e) { try { rpertoire = (Rpertoire) mapperXMLObjets.unmarshal(new
File("Tlphones.xml")); rpertoire.miseAJour(liste); } catch
(JAXBException ex) { setTitle("Impossible d'enregistrer le document
XML"); } } }); barre.add(new AbstractAction("Enregistrer", new
ImageIcon("enregistrer.gif")) {
-
public void actionPerformed(ActionEvent e) { try {
mapperObjetsXML.marshal(rpertoire, new File("Tlphones.xml")); }
catch (JAXBException ex) { setTitle("Impossible d'enregistrer le
document XML"); } } }); add(barre, BorderLayout.NORTH);
panneau.add(new JLabel("Prnom : ")); panneau.add(prnom);
panneau.add(new JLabel("Tlphone : ")); panneau.add(typeTlphone);
panneau.add(tlphone); add(panneau); add(liste, BorderLayout.SOUTH);
pack(); setLocationRelativeTo(null); setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); }
private void documentXML() { try { JAXBContext contexte =
JAXBContext.newInstance(Rpertoire.class); mapperObjetsXML =
contexte.createMarshaller();
mapperObjetsXML.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
true); mapperXMLObjets = contexte.createUnmarshaller(); } catch
(JAXBException ex) { setTitle("Impossible de crer le document
XML"); } }
public static void main(String[] args) throws ParseException {
new GestionRpertoire(); }}
enum TypeTlphone {Mobile, Domicile, Travail};
@XmlRootElementclass Rpertoire { @XmlElement(name="tlphones")
@XmlJavaTypeAdapter(MapAdapter.class) private TreeMap rpertoire =
new TreeMap();
public TreeMap getRpertoire() { return rpertoire; } public void
suppression(String prnom) { rpertoire.remove(prnom); }
public void ajout(String prnom, Object type, String numro) {
EnumMap tlphones; if (rpertoire.containsKey(prnom)) { tlphones =
rpertoire.get(prnom); tlphones.put((TypeTlphone)type, numro); }
else { tlphones = new EnumMap(TypeTlphone.class);
tlphones.put((TypeTlphone)type, numro); rpertoire.put(prnom,
tlphones); } }
public void miseAJour(JComboBox liste) { liste.removeAllItems();
for (Map.Entry carte : rpertoire.entrySet()) liste.addItem(carte);
}}
class MapAdapter extends XmlAdapter { @Override public TreeMap
unmarshal(Tlphones tlphones) throws Exception { TreeMap cartes =
new TreeMap(); for (Tlphone tlphone : tlphones.getTlphones()) {
EnumMap liste= new EnumMap(TypeTlphone.class); for (NumroParType
npt : tlphone.getTlphone()) liste.put(npt.getType(),
npt.getNumro()); cartes.put(tlphone.getPrnom(), liste); } return
cartes; } @Override public Tlphones marshal(TreeMap valeurs) throws
Exception { Tlphones tlphones = new Tlphones(); for (Map.Entry
carte : valeurs.entrySet()) { Tlphone personne = new
Tlphone(carte.getKey()); for (Map.Entry tlphone :
carte.getValue().entrySet())
personne.ajoutTlphone(tlphone.getKey(), tlphone.getValue());
tlphones.getTlphones().add(personne); } return tlphones; }}
class Tlphones { @XmlElement(name="personne") private ArrayList
tlphones = new ArrayList(); public ArrayList getTlphones() { return
tlphones; }}
class Tlphone { @XmlAttribute private String prnom; @XmlElement
private ArrayList tlphone = new ArrayList();
public Tlphone(String prnom) { this.prnom = prnom; } public
Tlphone() {}
-
public String getPrnom() { return prnom; }
public ArrayList getTlphone() { return tlphone; }
public void ajoutTlphone(TypeTlphone type, String numro) {
tlphone.add(new NumroParType(type, numro)); }}
class NumroParType { @XmlAttribute private String numro;
@XmlAttribute private TypeTlphone type;
public NumroParType() {} public NumroParType(TypeTlphone type,
String numro) { this.type = type; this.numro = numro; }
public String getNumro() { return numro; } public TypeTlphone
getType() { return type; }}
Il existe une annotation @XmlEnum, mais vous remarquez qu'il n'a
pas t ncessaire de la mettre en oeuvre. JAXB se dbrouille trs bien
sans et propose les numrateurs sousforme de chanes de caractres,
ceci automatiquement.
Gnrer un schma partir de classes compiles
L'outil schemagen fourni avec l'implmentation par dfaut permet
de gnrer un schma XML partir de classes annotes compiles.
Exemple d'application qui va servir de base pour la cration d'un
schma XML
Nous allons reprendre l'application du trac de formes qui permet
de sauvegarder, et donc de restituer un ensemble de formes, dans un
document XML.
Cette fois-ci nous devons proposer des attributs, respectivement
x et y, qui vont reprsenter les coordonnes dela balise .
Mise en place du source relatif aux formes
package jaxb;
import java.awt.Graphics2D;import java.util.ArrayList;import
javax.xml.bind.annotation.*;
// Dfinition de l'lment racine du document et de ses
sous-lments@XmlRootElementpublic class Formes { @XmlElements({
@XmlElement(name = "carr", type = Carr.class), @XmlElement(name =
"cercle", type = Cercle.class) }) private ArrayList formes = new
ArrayList();
public ArrayList getFormes() { return formes; } public void
ajoutForme(Forme forme) { formes.add(forme); } public void
supprimerFormes() { formes.clear(); }}
// La classe Forme est modifie pour accueillir un centre la
place des coordonnes x et yabstract class Forme { protected Centre
centre; public Forme() { centre = new Centre(); } public Forme(int
x, int y) { centre = new Centre(x, y); } public Centre getCentre()
{ return centre; } public void setCentre(Centre centre) {
this.centre = centre; } abstract void dessine(Graphics2D
surface);}
// Nouvel lment reprsenant le qui possde deux attributs x et
yclass Centre { @XmlAttribute private int y; @XmlAttribute private
int x;
public Centre() {} public Centre(int x, int y) { this.x = x;
this.y = y; } public int getX() { return x; } public int getY() {
return y; }
-
}class Cercle extends Forme { private int rayon = 50; public
Cercle() {} public Cercle(int x, int y, int rayon) { super(x, y);
this.rayon = rayon; } public int getRayon() { return rayon; }
public void setRayon(int rayon) { this.rayon = rayon; } @Override
void dessine(Graphics2D surface) { int x = centre.getX(); int y =
centre.getY(); surface.drawOval(x-rayon, y-rayon, 2*rayon,
2*rayon); }}
class Carr extends Forme { private int ct = 100; public Carr()
{} public Carr(int x, int y, int ct) { super(x, y); this.ct = ct; }
public int getCt() { return ct; } public void setCt(int ct) {
this.ct = ct; } @Override void dessine(Graphics2D surface) { int x
= centre.getX(); int y = centre.getY(); surface.drawRect(x-ct/2,
y-ct/2, ct, ct); }}
Utilisation de l'utilitaire schemagen
Schema1.xsd