XStream, ou comment sérialiser un objet java en XML

January 19th, 2005 par Simon

La version 1.1 de XStream vient de sortir. Cet outil open-source permet de sérialiser/désérialiser un objet java en XML. Comment ca marche ? A quoi ca sert ?

Le plus simple est de commencer par un exemple :
public class Person {
protected String lastname = null;
protected String firstname = null;
protected Date birthday = null;
protected ArrayList phones = new ArrayList(); // liste de Phone
protected transient String id = null;
// ……
}
public class Phone {
protected int code;
protected String number;
// ……
}

Je vous passe les instanciations de l’objet ’simon’ (type Person). Le simple code suivant :

XStream xstream = new XStream();
System.out.println(xstream.toXML(simon));

affiche :

<person>
<lastname>titi</lastname>
<firstname>toto</firstname>
<birthday>2005-01-19 11:13:27.670 CET</birthday>
<phones>
<phone>
<code>4</code>
<number>50527276</number>
</phone>
<phone>
<code>6</code>
<number>70211088</number>
</phone>
</phones>
</person>

Nous avons donc une vue XML de l’objet ’simon’. Vous pouvez remarquer la simplicité de mise en oeuvre. En effet il n’y a aucune contrainte sur le code des classes à sérialiser :

  • pas besoin d’implémenter l’interface Serializable
  • pas besoin d’être un JavaBean (définition de méthodes getXXX() et setXXX())
  • pas besoin d’avoir un constructeur par défaut

Les autres points forts sont :

  • threadsafe
  • optimisé pour la JVM 1.4.2 (mais fonctionne quand même en 1.3)
  • utilisation d’un parser JAXP (par défaut XPP3, à priori ultra light et rapide).
  • extensible (possiblité de sérialiser byte[] en code 64, modifier le nom des noeuds, …)
  • les champs “transient” ne sont pas sérialisés

Pour ceux qui connaissent java.beans.XMLEncoder/XMLDecoder du JDK 1.4.X, il n’y a pas photo :

  • n’encode/décode que les JavaBeans
  • le XML généré n’est pas très “human readly” :

    <?xml version=”1.0″ encoding=”UTF-8″?>
    <java version=”1.0″ class=”java.beans.XMLDecoder”>
    <object class=”Person”>
    <void property=”firstname”>
    <string>toto</string>
    </void>
    <void property=”lastname”>
    <string>titi</string>
    </void>

    ……

    </object>
    </java>

Donc maintenant que vous avez compris comment ca marchait, vous vous demandez à quoi ca sert ? Et bien ce qu’il faut bien comprendre, c’est que XStream n’est pas un outil de mapping JAVA/XML comme castorxml, jaxb, … Il n’y a aucun mécanisme de validation,et la définition du XML est donnée par la classe Java.
Des exemples d’application sont :

  • transport des données
  • configuration d’une application
  • affichage de l’état d’un objet, par exemple lors d’un log d’erreur. Cela évite de coder et surtout maintenir la méthode toString().

Liens :

6 Responses to “XStream, ou comment sérialiser un objet java en XML”

  1. Freddy Mallet Says:

    Merci simon, l’idée d’éviter de coder et surtout de maintenir la méthode toString() risque d’être appliquée dès demain. En effet actuellement je passe aux EJB des beans de définition de requêtes qu’il faut bien sûr que j’arrive à logger en cas de problème pour connaître le contexte au moment de l’erreur. Bien évidemment je surcharge pour cela la méthode toString().

    Pour info, il me semble que le concurrent direct de xStream est Betwixt chez Jakarta

  2. Franck Says:

    Un petit commentaire pour ne pas oublier que pour implémenter facilement la méthode toString, il ne faut pas oublier nos amis de Jakarta avec la lib commons-lang et le ToStringBuilder.
    L’exemple tiré de la doc ToStringBuilder:

    public class Person {
    String name;
    int age;
    boolean isSmoker;

    ...

    public String toString() {
    return new ToStringBuilder(this).
    append("name", name).
    append("age", age).
    append("smoker", smoker).
    toString();
    }
    }

    System.out.println(aPerson); => Person@7f54[name=Stephen,age=29,smoker=false]

    Person@7f54[name=Stephen,age=29,smoker=false] tiens en une seule ligne, cool et plus lisible pour les logs que du XML.
    Mais si vous êtes un peu fénéant, en s’appuyant sur la réflexion on obtient la même chose en moins de ligne de code:

    public String toString() {
    return ToStringBuilder.reflectionToString(this);
    }

    Je viens même de découvrir à l’instant que ça fonctionne sur des objets simples ! :

    System.out.println("An object: " + ToStringBuilder.reflectionToString(anObject));

    Pour finir, vous pouvez même customizer l’affichage des différents types avec le ToStringStyle.

  3. Freddy Mallet Says:

    Puisque vous m’avez l’air chaud comme de la braise, est-ce que vous connaissez un moyen simple de surcharger les méthodes Object.hashCode(), Object.equals() ? Je m’explique:

    Je transmets à mes EJBs des beans de définition de requête (que j’appelle filtre) plus ou moins complexes. Côté client j’accède à mes EJBs au travers d’objets encapsulant la complexité d’appel (Business Delegate Pattern). Ce s objets assurent également un mécanisme de cache dont les clefs sont mes beans de filtre. Par conséquent ces beans de filtres doivent surcharger les méthodes Object.hashCode(), Object.equals(), ce que j’ai fait et qui marche. Le seul inconvénient c’est que c’est un peu lourd à faire sur tous mes filtres et en plus c’est source de bug (mais les tests unitaires veillent aux grains), du style :

    public int hashCode() {
    int hashCode = 0;
    hashCode += portfolios.hashCode();
    if (currency != null) {
    hashCode += currency.hashCode();
    }
    if (benchmark != null) {
    hashCode += benchmark.hashCode();
    }
    hashCode += startDate.getMonth() + startDate.getYear();
    hashCode += endDate.getMonth() + endDate.getYear();
    hashCode += userId.hashCode();
    return hashCode;
    }

    Quelqu’un n’aurait pas une idée du style HashCodeBuilder.reflectionHashCode(anObject) ? (En sachant que les temps de réponse sur ces méthodes doivent être plus qu’excellents)

  4. Freddy Mallet Says:

    OUUUUUUUUUPS !!!!
    J’aurais mieux fait de jeter un oeil sur tout commons-lang avant de l’ouvrir….

  5. Eric Says:

    Puisque l’on a dévié du sujet de XStream (merci de l’exemple Simon), et que nous sommes rentré dans le domaine caché de Jakarta (les commons), je vous invite à regarder les classes functors de commons-collection, les classes functor permettent de coder élegament certains problèmes. Un petit exemple est disponible sur OnJava. — Freddy je t’arrête tout de suite, je sais… je suis feignant et ça aurait fait une entrée de blog en plus, mais je pense à toi et je te fais travailler ton anglais.

  6. Franck Says:

    Je crois que le bon lien sur ONJava est http://www.onjava.com/pub/a/onjava/2004/12/22/jakarta-gems-1.html?page=2

Leave a Reply

You must be logged in to post a comment.