UP | HOME

JAXB - Les adaptateurs

Par défaut, la norme JAXB n'autorise la traduction que vers une classe : il n'est pas possible de traduire vers une interface. La raison est simple : il n'est pas possible de construire un objet d'une interface sans connaître une classe d'implémentation. Il est cependant possible d'utiliser un adaptateur pour permettre la traduction d'un document vers un type interface. Autre limitation dans l'autre sens, il n'est pas possible de traduire une classe non annotée en un document. Une première solution est d'utiliser la classe enveloppe JAXBElement. Une seconde solution est d'utiliser ici aussi un adpatateur.

Avec JAX-RS, on utilise des adaptateurs pour les traductions non seulement entre les objets et les documents, mais aussi entre les objets et les chemins (faisant partie des URI des ressources). Le problème est analogue au précédent et requiert de recourir aux adaptateurs.

Table of Contents

Utilisation des adpatateurs

Il est nécessaire d'utiliser un adaptateur lorsqu'une des erreurs suivantes survient.

  • No message body writer/reader has been found for response/request class/type X : il faut alors annoter X.
  • Parameter Class X has no constructor with single String parameter, static valueOf(String) or fromString(String) methods : il faut alors annoter X.

Pour annoter X, on utilise l'annotation @XmlJavaTypeAdapter(A.class), où A est un adaptateur d'une classe héritant de XmlAdapter (cf. paquet javax.xml.bind.annotation.adapters).

Distinguons les deux cas d'usage.

Liaison objet-document (data binding).

Hypothèses

Soient I une interface, A et B deux classes implémentant I telles que A n'est pas traduisible (marshallable) alors que B l'est. On suppose que l'adaptateur implémente deux méthodes :

  • B marshall(I x),
  • I unmarshall(B x),

B implémente I et est traduisible.

Problème 1 : marshalling (envoi)

Un problème survient lorsqu'un A doit être traduit en un document.

Solution 1

On annote I par un adaptateur paramétré par I et B. La méthode B marshall(I x) est appelée, ce qui permet de transformer le A en un B traduisible.

Problème 2 : unmarshalling (réception)

A la réception d'un document à utiliser pour construire un I, aucune classe d'implémentation n'est connue par le data binder.

Solution 2

On annote I par un adaptateur paramétré par I et B. La classe B utilisée par l'adaptateur sert à la traduction inverse (unmarshalling), ce qui produit un B, puis la méthode I unmarshall(B x) est appelée pour le convertir en un I : comme dans notre cas B est un sous-type de I, la méthode correspond à une simple conversion implicite.

Liaison objet-chemin

Hypothèses

Soient I une interface, A et B deux classes implémentant I telles que A ne peut pas être construit à partir d'une String alors que B peut l'être. On suppose que l'adapteur implémente deux méthodes :

  • B marshall(I x),
  • I unmarshall(B x),

B implémente I et peut être construit à partir d'une String (B possède un constructeur B(String), ou une fonction B valueOf(String), ou une fonction B fromString(String)).

Problème 1 : traduction d'un objet en un chemin

Lors de la traduction d'un objet de type I en un chemin, c'est la méthode toString() qui est appelée. Il n'y a donc jamais de problème, qu'on utilise un A ou un B.

Solution 1

Rien à faire.

Problème 2 : traduction d'un chemin en un objet

Un problème survient lors de la traduction inverse, d'un chemin vers objet de type I : on ne sait pas construire un I à partir d'une String.

Solution 2

On annote I par un adaptateur paramétré par I et B. La classe B utilisée par l'adaptateur sert à la construction d'un objet B, en utilisant le constructeur ou les fonctions prenant une String en argument. La méthode I unmarshall(B x) est ensuite appelée pour produire un I (par conversion implicite dans notre cas).

Implémentation des adaptateurs

Pour implémenter un adaptateur, on hérite de la classe générique XmlAdapter<T, NT>, où T est le type traduisible et NT celui non traduisible. On implémente deux méthodes, marshall et unmarshall, suivant le modèle suivant.

public class Adaptateur extends
  javax.xml.bind.annotation.adapters.XmlAdapter<T, NT> {

    @Override
    public T marshal(NT a) throws Exception {
      // Construction d'un objet traduisible b à partir de a 
      ...
      return b;
    }
    @Override
    public NT unmarshal(T b) throws Exception {
      // Construction d'un objet non traduisible a à partir de b 
      ...
      return a;
    }
}

Très souvent, le type T correspond à une classe d'implémentation de l'interface NT. Dans ce cas, les deux méthodes se simplifient souvent ainsi.

public class Adaptateur extends
  javax.xml.bind.annotation.adapters.XmlAdapter<T, NT> {

    @Override
    public T marshal(NT a) throws Exception {
      T b = new T();
      b.setX(a.getX()); // pour chaque propriété X à traduire
      return b;
    }
    @Override
    public NT unmarshal(T b) throws Exception {
      return b; // conversion implicite
    }
}

Last Updated 2016-04-08T11:10+0200. Comments or questions: Send a mail.