web-dev-qa-db-fra.com

Comment réutiliser le JSON / JAXB de Jersey pour la sérialisation?

J'ai un service JAX-RS REST implémenté à l'aide de Jersey. L'une des fonctionnalités intéressantes de JAX-RS/Jersey est la facilité avec laquelle un POJO peut être transformé en service REST, simplement en saupoudrant quelques annotations Java ... y compris un mécanisme trivialement facile pour traduire les POJO en JSON - à l'aide d'annotations JAXB.

Maintenant, j'aimerais pouvoir profiter de cette fonctionnalité intéressante de JSON à des fins non REST - j'aimerais pouvoir simplement sérialiser certains de ces objets sur le disque, en tant que texte JSON. Voici un exemple d'objet JAXB que je voudrais sérialiser:

@XmlRootElement(name = "user")
public class UserInfoImpl implements UserInfo {

    public UserInfoImpl() {} 

    public UserInfoImpl(String user, String details) {
        this.user = user;
        this.details = details;
    }

    public String getUser() { return user; }
    public void setUser(String user) { this.user = user; }

    public String getDetails() { return details; }
    public void setDetails(String details) { this.details = details; }

    private String user;
    private String details;
}

Jersey peut transformer l'un d'entre eux en json sans informations supplémentaires. Je me demande si Jersey a exposé cette fonctionnalité dans l'API pour des besoins comme le mien? Je n'ai pas eu de chance de le trouver jusqu'à présent ...

Merci!

MISE À JOUR 2009-07-09: J'ai appris que je peux utiliser l'objet Providers pour presque faire exactement ce que je veux:

  @Context Providers ps;
  MessageBodyWriter uw = ps.getMessageBodyWriter(UserInfoImpl.class, UserInfoImpl.class, new Annotation[0], MediaType.APPLICATION_JSON_TYPE);

  uw.writeTo(....)

... Cela écrit l'objet en tant que json dans n'importe quel flux de sortie, ce qui serait parfait pour moi, mais je ne peux accéder à l'objet Providers qu'en utilisant @Context à partir d'un objet @Component. Quelqu'un sait-il comment y accéder à partir d'un POJO régulier et non annoté? Merci!

28
ctwomey

Jersey utilise plusieurs cadres différents selon que vous utilisez la notation mapped (), badgerfish () ou natural (). Le naturel est généralement celui que les gens veulent. Et cela est implémenté en utilisant le très bon (et très rapide) processeur Jackson JSON autonome, je crois, qui va de Object-> JAXB-> JSON. Cependant Jackson fournit également son propre fournisseur JAX-RS pour aller directement Object-> JSON.

En fait, ils ont même ajouté la prise en charge des annotations JAXB. Jettes un coup d'oeil à

http://wiki.fasterxml.com/JacksonJAXBAnnotations

Je pense que c'est finalement ce que vous recherchez. Jackson fait le traitement Object <-> JSON ... Jersey fait juste les appels pour vous

19
Andy O

Voici un bref exemple simple d'utilisation de JAXB pour mapper des objets à JSON (à l'aide de Jackson):

http://ondra.zizka.cz/stranky/programovani/Java/jaxb-json-jackson-howto.texy

6
Ondra Žižka
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pojoObject);
5
RimasK

Les annotations JAXB fonctionnent correctement lors de la sérialisation en XML. Le problème principal est que JAXB ne prend pas en charge les tableaux vides. Donc, lors de la sérialisation de quelque chose comme ça ...

List myArray = new ArrayList();

... vers json via jaxb anottations, tous vos tableaux vides deviennent nuls au lieu de [].

Pour résoudre ce problème, vous pouvez simplement sérialiser vos pojos directement en json via jackson.

Jetez un œil à cela dans le guide de l'utilisateur de Jersey: http://jersey.Java.net/nonav/documentation/latest/user-guide.html#d0e1959

C'est le meilleur moyen d'utiliser le fournisseur Jackson sans JAXB. De plus, vous pouvez toujours utiliser la dernière version de jackson en téléchargeant jackson-all-x.y.z-jar depuis son site Web.

Cette méthode n'interférera pas avec vos annotations jaxb donc je vous suggère d'essayer!

3
ramon_salla

Avec un petit amorçage spécifique à Jersey, vous pouvez l'utiliser pour créer les objets JSON nécessaires pour vous. Vous devez inclure les dépendances suivantes (vous pouvez utiliser le bundle, mais cela posera des problèmes si vous utilisez Weld pour les tests):

    <dependency>
        <groupId>com.Sun.jersey</groupId>
        <artifactId>jersey-json</artifactId>
        <version>1.12</version>
    </dependency>
    <dependency>
        <groupId>com.Sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.12</version>
    </dependency>

De là, vous pouvez créer une classe annotée JAXB. Ce qui suit est un exemple:

@XmlRootElement
public class TextMessage {
private String text;
    public String getText() { return text; }
    public void setText(String s) { this.text = text; }
}

Vous pouvez ensuite créer le test unitaire suivant:

    TextMessage textMessage = new TextMessage();
    textMessage.setText("hello");
    textMessage.setUuid(UUID.randomUUID());

    // Jersey specific start
    final Providers ps = new Client().getProviders();
    // Jersey specific end
    final MultivaluedMap<String, Object> responseHeaders = new MultivaluedMap<String, Object>() {

        @Override
        public void add(final String key, final Object value) {
        }

        @Override
        public void clear() {
        }

        @Override
        public boolean containsKey(final Object key) {
            return false;
        }

        @Override
        public boolean containsValue(final Object value) {
            return false;
        }

        @Override
        public Set<Java.util.Map.Entry<String, List<Object>>> entrySet() {
            return null;
        }

        @Override
        public List<Object> get(final Object key) {
            return null;
        }

        @Override
        public Object getFirst(final String key) {
            return null;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public Set<String> keySet() {
            return null;
        }

        @Override
        public List<Object> put(final String key, final List<Object> value) {
            return null;
        }

        @Override
        public void putAll(
                final Map<? extends String, ? extends List<Object>> m) {
        }

        @Override
        public void putSingle(final String key, final Object value) {
        }

        @Override
        public List<Object> remove(final Object key) {
            return null;
        }

        @Override
        public int size() {
            return 0;
        }

        @Override
        public Collection<List<Object>> values() {
            return null;
        }
    };

    final MessageBodyWriter<TextMessage> messageBodyWriter = ps
            .getMessageBodyWriter(TextMessage.class, TextMessage.class,
                    new Annotation[0], MediaType.APPLICATION_JSON_TYPE);
    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Assert.assertNotNull(messageBodyWriter);

    messageBodyWriter.writeTo(textMessage, TextMessage.class,
            TextMessage.class, new Annotation[0],
            MediaType.APPLICATION_JSON_TYPE, responseHeaders, baos);
    final String jsonString = new String(baos.toByteArray());
    Assert.assertTrue(jsonString.contains("\"text\":\"hello\""));

L'avantage de cette approche est qu'elle conserve tout dans l'API JEE6, aucune bibliothèque externe n'est explicitement nécessaire, sauf pour tester et obtenir les fournisseurs. Cependant, vous devez créer une implémentation de MultivaluedMap car il n'y a rien de prévu dans la norme et nous ne l'utilisons pas réellement. Il peut être également plus lent que GSON, et beaucoup plus compliqué que nécessaire.

1

Étant donné que Jersey est une implémentation de référence de JAX-RS et que JAX-RS se concentre entièrement sur la fourniture d'un moyen standard d'implémentation du point final pour le service REST), les problèmes de sérialisation de la charge utile sont laissés à d'autres normes.

Je pense que s'ils incluaient la sérialisation des objets dans la norme JAX-RS, elle deviendrait rapidement une grosse bête à plusieurs têtes qui serait difficile à mettre en œuvre et perdrait une partie de sa concentration.

J'apprécie à quel point Jersey est axé sur la livraison propre et simple à utiliser REST points de terminaison. Dans mon cas, je viens de sous-classer un parent qui a toute la plomberie JAXB afin de rassembler les objets entre binaire et XML est très propre.

1
Jack Cox

Je comprends les vues XML, mais cela aurait montré une certaine prévoyance d'exiger la prise en charge JSON des POJO en tant qu'équipement standard. Devoir ranger les identifiants JSON avec des caractères spéciaux n'a aucun sens si votre implémentation est JSON et votre client est un RIA JavaScript.

Aussi, pas que Java Les beans ne sont PAS des POJO. Je voudrais utiliser quelque chose comme ça sur la surface externe de mon niveau web:

public class Model
{
   @Property height;
   @Property weight;
   @Property age;
}

Pas de constructeur par défaut, pas de bruit getter/setter, juste un POJO avec mes propres annotations.

0
rickoshay