I. Introduction▲
XStream est une API Java qui permet de sérialiser et désérialiser des objets dans des fichiers XML.
Les avantages d'XStream sont principalement :
- La facilité d'utilisation de l'API.
- Pas de modification de code des objets que vous voulez sérialiser.
- La rapidité d'exécution et une faible utilisation de la mémoire.
Toutes les sources de cet article sont disponibles ici sous la forme d'un projet pour Eclipse.
II. Où trouver XStream ?▲
XStream est disponible à l'adresse suivante http://xstream.codehaus.org.
XStream est téléchargeable sous la forme d'une librairie JAR par le menu "Download" du site.
Pour la suite de l'article, nous utiliserons la dernière version stable xstream-x.x.jar obtenue sur le site ( Télécharger ici ).
La librairie xstream-x.x.jar est à mettre dans le classpath pour que les exemples fonctionnent.
III. Création des classes à sérialiser▲
Nous allons premièrement créer deux classes, Entete et Article.
La classe Article déclarera un attribut de classe de type Entete.
Ces deux classes contiennent les constructeurs, getters et setters utiles.
La classe Entete :
package
beans;
import
java.util.Date;
public
class
Entete {
private
String titre;
private
Date dateCreation;
public
Date getDateCreation
(
) {
return
dateCreation;
}
public
void
setDateCreation
(
Date dateCreation) {
this
.dateCreation =
dateCreation;
}
public
String getTitre
(
) {
return
titre;
}
public
void
setTitre
(
String titre) {
this
.titre =
titre;
}
public
Entete
(
String titre, Date dateCreation) {
super
(
);
this
.titre =
titre;
this
.dateCreation =
dateCreation;
}
}
Et la classe Article qui utilise la classe Entete :
package
beans;
public
class
Article {
private
Entete entete;
private
String synopsis;
public
String getSynopsis
(
) {
return
synopsis;
}
public
Article
(
Entete entete, String synopsis) {
super
(
);
this
.entete =
entete;
this
.synopsis =
synopsis;
}
public
void
setSynopsis
(
String synopsis) {
this
.synopsis =
synopsis;
}
public
Entete getEntete
(
) {
return
entete;
}
public
void
setEntete
(
Entete entete) {
this
.entete =
entete;
}
}
IV. Sérialisation des classes▲
Ci-dessous une classe Serialisation avec une méthode main contenant le code pour convertir les classes précédentes en chaîne XML :
package
tests;
import
java.util.Date;
import
com.thoughtworks.xstream.XStream;
import
com.thoughtworks.xstream.io.xml.DomDriver;
public
class
Serialisation {
public
static
void
main
(
String[] args) {
// Instanciation de la classe XStream
XStream xstream =
new
XStream
(
new
DomDriver
(
));
// Instanciation de la classe Entete
Entete entete =
new
Entete
(
"Titre de l'article"
,new
Date
(
));
// Instanciation de la classe Article
Article article =
new
Article
(
entete,"Un synopsis bien placé !!! <strong>avec une balise HTML</strong>"
);
// Convertion du contenu de l'objet article en XML
String xml =
xstream.toXML
(
article);
// Affichage de la conversion XML
System.out.println
(
xml);
}
}
La console affichera :
<beans.Article>
<entete>
<titre>
Titre de l'
article</titre>
<dateCreation>
2006-10-01 18:00:31.187 CEST</dateCreation>
</entete>
<synopsis>
Un synopsis bien placé !!! <
strong>
avec une balise HTML<
/strong>
</synopsis>
</beans.Article>
XStream convertit le contenu de l'objet article en un joli fichier XML, on remarquera que les caractères spéciaux sont également traités :
- ' en '
- <strong> en <strong>
Maintenant, pour sérialiser l'objet article en un fichier article.xml, le code de la classe serait :
package
tests;
import
java.io.File;
import
java.io.FileNotFoundException;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.util.Date;
import
beans.Article;
import
beans.Entete;
import
com.thoughtworks.xstream.XStream;
import
com.thoughtworks.xstream.io.xml.DomDriver;
public
class
Serialisation {
public
static
void
main
(
String[] args) {
try
{
// Instanciation de la classe XStream
XStream xstream =
new
XStream
(
new
DomDriver
(
));
// Instanciation de la classe Entete
Entete entete =
new
Entete
(
"Titre de l'article"
, new
Date
(
));
// Instanciation de la classe Article
Article article =
new
Article
(
entete, "Un synopsis bien placé !!! <strong>avec une balise HTML</strong>"
);
// Instanciation d'un fichier c:/temp/article.xml
File fichier =
new
File
(
"c:/temp/article.xml"
);
// Instanciation d'un flux de sortie fichier vers
// c:/temp/article.xml
FileOutputStream fos =
new
FileOutputStream
(
fichier);
try
{
// Sérialisation de l'objet article dans c:/temp/article.xml
xstream.toXML
(
article, fos);
}
finally
{
// On s'assure de fermer le flux quoi qu'il arrive
fos.close
(
);
}
}
catch
(
FileNotFoundException e) {
e.printStackTrace
(
);
}
catch
(
IOException ioe) {
ioe.printStackTrace
(
);
}
}
}
V. Désérialisation des classes▲
Maintenant pour recharger (désérialiser) un objet de type Article avec les données sérialisées dans le chapitre précédent, le code sera :
package
tests;
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.FileNotFoundException;
import
java.io.IOException;
import
beans.Article;
import
com.thoughtworks.xstream.XStream;
import
com.thoughtworks.xstream.io.xml.DomDriver;
public
class
Deserialisation {
public
static
void
main
(
String[] args) {
try
{
// Instanciation de la classe XStream
XStream xstream =
new
XStream
(
new
DomDriver
(
));
// Redirection du fichier c:/temp/article.xml vers un flux
// d'entrée fichier
FileInputStream fis =
new
FileInputStream
(
new
File
(
"c:/temp/article.xml"
));
try
{
// Désérialisation du fichier c:/temp/article.xml vers un nouvel
// objet article
Article nouvelArticle =
(
Article) xstream.fromXML
(
fis);
// Affichage sur la console du contenu de l'attribut synopsis
System.out.println
(
nouvelArticle.getSynopsis
(
));
}
finally
{
// On s'assure de fermer le flux quoi qu'il arrive
fis.close
(
);
}
}
catch
(
FileNotFoundException e) {
e.printStackTrace
(
);
}
catch
(
IOException ioe) {
ioe.printStackTrace
(
);
}
}
}
VI. Un peu plus...▲
Dans ce chapitre, quelques informations et exemples pour aller plus loin avec l'API XStream.
VI-A. Comment empêcher la sérialisation d'un attribut ?▲
En déclarant un attribut comme transient, celui-ci ne sera pas sérialisé.
Dans l'exemple suivant l'attribut numerocb ne sera donc pas sérialisé :
package
beans;
public
class
Auteur {
private
String nom;
private
String prenom;
private
transient
String numerocb;
//...constructeurs, setters et getters
}
VI-B. La sérialisation de collections▲
La sérialisation de collections avec XStream se fait très facilement, prenons pour exemple une classe Magasin contenant une liste d'objets :
package
beans;
import
java.util.ArrayList;
import
java.util.List;
public
class
Magasin {
private
String nom;
// Supprimer les "<Object>" si vous utilisez un jdk < 1.5
List<
Object>
objets =
new
ArrayList<
Object>(
);
//...constructeurs, setters et getters
}
Et le code suivant, qui peuplera la classe Magasin avec des objets de type Article et Auteur :
package
tests;
import
java.util.ArrayList;
import
java.util.Date;
import
java.util.List;
import
beans.Article;
import
beans.Auteur;
import
beans.Entete;
import
beans.Magasin;
import
com.thoughtworks.xstream.XStream;
import
com.thoughtworks.xstream.io.xml.DomDriver;
public
class
CollectionSerialisation {
public
static
void
main
(
String[] args) {
// Instanciation de la classe XStream
XStream xstream =
new
XStream
(
new
DomDriver
(
));
// Instanciation de la classe Entete
Entete entete =
new
Entete
(
"Titre de l'article"
, new
Date
(
));
// Instanciation de la classe Article
Article article =
new
Article
(
entete, "Un synopsis bien placé !!! <strong>avec une balise HTML</strong>"
);
// Instanciation de la classe Auteur
Auteur auteur =
new
Auteur
(
"LA LA LA LA LA LA"
, "Starsky & Hutch"
);
auteur.setNumerocb
(
"1788 1803 0485 1875"
);
// Instanciation de la classe Magasin
Magasin magasin =
new
Magasin
(
"Yatoutici"
);
List objets =
new
ArrayList
(
);
objets.add
(
article);
objets.add
(
auteur);
magasin.setObjets
(
objets);
// Convertion du contenu de l'objet article en XML
String xml =
xstream.toXML
(
magasin);
// Affichage de la conversion XML
System.out.println
(
xml);
}
}
La console affichera :
<beans.Magasin>
<nom>
Yatoutici</nom>
<objets>
<beans.Article>
<entete>
<titre>
Titre de l'
article</titre>
<dateCreation>
2006-10-01 17:48:48.78 CEST</dateCreation>
</entete>
<synopsis>
Un synopsis bien placé !!! <
strong>
avec une balise HTML<
/strong>
</synopsis>
</beans.Article>
<beans.Auteur>
<nom>
LA LA LA LA LA LA</nom>
<prenom>
Starsky &
Hutch</prenom>
</beans.Auteur>
</objets>
</beans.Magasin>
VI-C. Comment créer des alias pour les noms de package ?▲
Vous avez dû remarquer qu'XStream utilisait les noms des packages pour nommer les balises XML, ce n'est pas bien lisible.
Heureusement, XStream propose un système permettant de créer des alias sur les classes utilisées lors de la sérialisation.
Ainsi en reprenant notre premier exemple de sérialisation, on créera les alias de la manière suivante :
...
// Convertion du contenu de l'objet article en XML
xstream.alias
(
"article"
, Article.class
);
xstream.alias
(
"entete"
, Entete.class
);
String xml =
xstream.toXML
(
article);
// Affichage de la conversion XML
System.out.println
(
xml);
...
La console affichera les balises substituées avec les alias :
<article>
<entete>
<titre>
Titre de l'
article</titre>
<dateCreation>
2006-10-01 18:08:06.234 CEST</dateCreation>
</entete>
<synopsis>
Un synopsis bien placé !!! <
strong>
avec une balise HTML<
/strong>
</synopsis>
</article>
VII. XStream : un outil de persistance ?▲
A cette question, je répondrai : oui et non ! Une réponse de normand me direz vous, je m'explique :
Alors oui, car XStream permet effectivement d'enregistrer, puis de recouvrer des objets sérialisés, mais cela uniquement en utilisant une sérialisation XML (A noter à ce propos qu'XStream propose des classes très utiles comme XmlArrayList, XmlSet, ou XmlMap).
Et non, parce que j'associe persistance de données avec SGBD et ça XStream ne sait pas faire. Pour cela il faudra utiliser des outils comme Hibernate, Castor, JPox, iBatis, OJB, etc. et certains de ces outils savent utiliser les deux formes de stockage (SGBD ou XML) pour la persistance.
Mais si vous optez pour une persistance XML avec XStream alors sachez :
- Qu'aucune description n'est nécessaire pour sérialiser la plupart des objets (pas de "mapping")
- XStream utilise l'introspection pour retrouver les propriétés d'un bean, elles pourront être déclarées private, ne pas avoir de getters ou setters et XStream retrouvera ses petits !
VIII. Conclusion▲
A l'utilisation d'XStream, on remarque très vite que cette API va rendre de nombreux services.
XStream est une librairie idéale pour les néophytes du DOM et autre SAX, ils trouveront plus de facilités dans la création et les manipulations de fichiers XML en utilisant XStream.
Et les finalités d'XStream sont très nombreuses, le transport de données, utilisation de fichiers de configuration, etc.
Pour exemple d'utilisation, j'ai préconisé XStream pour transporter toute une configuration paramétrée dans une base de données vers une application embarquée devant fonctionner en mode déconnecté.
Télécharger les sources de l'article
IX. Remerciements▲
Un grand merci à le y@m's pour la relecture de cet article et à GrandFather pour sa bonne idée.