lundi 27 mai 2013

Modifier le rendu d'une liste de string avec XStream en utilisant un converter

Continuons avec la classe Toto contient une liste de String dont le rendu est le suivant :
<liste__Produit>
  <string>truc</string>
  <string>bidule</string>
</liste__Produit>

Plutôt que d'avoir une suite de balise string, il serait souhaitable d'avoir une balise part.
Dans la documentation, il existe l'annotation @XStreamImplicit.
Il y a deux paramètres : itemFieldName et keyFieldName.
Le premier indique l'alias de la collection et le deuxième sert pour les type Map.
Avec le code suivant :
@XStreamAlias("listeProduit")
@XStreamImplicit(itemFieldName="part")
public List titi = new ArrayList() ;

Le résultat est à présent :
<produit>
  <id>1</id>
  <part>truc</part>
  <part>bidule</part>
  <dateEnvoie>2013-03-01 10:12:04.306 UTC</dateEnvoie>
</produit>

La liste est donc mise à plat avec une suite d'entrée.
Si il est désiré d'avoir une balise parente, il va falloir passer par un converter maison.
Voici l'annotation :
@XStreamAlias("listeProduit")
@XStreamConverter(value=ListToStringXStreamConverter.class, strings={"part"}) 
public List titi = new ArrayList() ;

Avant de continuer, voyons un peu le principe.
value indique le nom de la classe de conversion.
strings est quant à lui disponible dans l'annotation pour des besoins annexes. Ce paramètre sera transmis au constructeur. D'autres champs sont mis à disposition et peuvent être exploités pour des besoins spécifiques comme types qui permet de contenir des classes.
// Come from http://stackoverflow.com/questions/1791178/customising-serialisation-of-java-collections-using-xstream
public class ListToStringXStreamConverter implements Converter {

private String alias;

public ListToStringXStreamConverter(String alias) {
    super();
    this.alias = alias;
}

@SuppressWarnings("rawtypes")
@Override
public boolean canConvert(Class type) {
    return true;
}

@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {

    @SuppressWarnings("unchecked")
    List list = (List)source;

    for (String string : list) {
        writer.startNode(alias);
        writer.setValue(string);
        writer.endNode();
    }
}

@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
    throw new UnsupportedOperationException("ListToStringXStreamConverter does not offer suport for unmarshal operation");
}

Et mainteant, voici le rendu :
<produit>
  <id>1</id>
  <listeProduit>
    <part>truc</part>
    <part>bidule</part>
  </listeProduit>
  <dateEnvoie>2013-03-01 10:25:19.10 UTC</dateEnvoie>
</produit>

Ce qui est fait, c'est de remplacer le composant qui affiche une liste par le notre.
Par défaut, XStream écrit l'alias du champs et ensuite le converter écrit les données dans le flux. Pour cela, il prend en paramètre le nom du noeud à créer ("part").

Aucun commentaire:

Enregistrer un commentaire