Comment réutiliser le JSON/JAXB De Jersey pour la sérialisation?
j'ai un service de repos JAX-RS implémenté en utilisant Jersey. Une des caractéristiques cool de JAX-RS/Jersey est la facilité avec laquelle un POJO peut être transformé en un service de repos, simplement en saupoudrant quelques annotations Java... incluant un mécanisme trivialement facile pour traduire les POJOs en JSON-en utilisant les annotations JAXB.
maintenant, je voudrais pouvoir profiter de cette fonctionnalité de JSON-ifying cool pour des buts non-REST - j'aimerais être en mesure de sérialiser certains de ces objets sur le disque, comme JSON text. 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'eux en json sans aucune information supplémentaire. Je me demande si Jersey a exposé cette fonctionnalité dans L'API pour des besoins comme le mien? Je n'ai pas réussi à le trouver jusqu'à présent...
Merci!
mise à jour 2009-07-09: j'ai appris que je peux utiliser les Fournisseurs de l'objet 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(....)
... Ceci écrit l'objet comme json à n'importe quel outputstream, ce qui serait parfait pour moi, mais je ne peux obtenir l'objet Providers qu'en utilisant @Context à partir d'un objet @Component. Est-ce que quelqu'un sait comment y accéder à partir d'un POJO régulier et non annoté? Merci!
7 réponses
Jersey utilise un couple de cadres différents selon que vous utilisez mapped(), badgerfish(), ou natural() notation. Naturel est généralement celui que les gens veulent. Et c'est mis en œuvre en utilisant le Très bon (et très rapide) processeur autonome Jackson JSON, je crois, qui va de L'objet->JAXB->JSON. Cependant Jackson fournit également son propre fournisseur JAX-RS pour aller objet direct - > JSON.
en fait, ils ont même ajouté le support pour les annotations JAXB. Avoir un coup d'oeil
http://wiki.fasterxml.com/JacksonJAXBAnnotations
je pense que c'est finalement ce que vous cherchez. Jackson ne objet < ->JSON processing...Jersey fait juste les appels pour vous
voici un bref exemple simple d'utilisation de JAXB pour mapper des objets vers JSON (en utilisant Jackson):
http://ondra.zizka.cz/stranky/programovani/java/jaxb-json-jackson-howto.texy
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pojoObject);
les annotations JAXB fonctionnent bien lors de la sérialisation vers XML. Le problème principal est que JAXB ne supporte pas les tableaux vides. Donc quand on sérialise quelque chose comme ça...
List myArray = new ArrayList();
...en json via jaxb anottations tous vos tableaux vides devenir null au lieu de [].
pour résoudre ceci vous pouvez sérialiser vos pojos directement à json via jackson.
jetez un oeil à ceci de L'utilisateur de Jersey guide: http://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e1959
C'est la meilleure façon d'utiliser Jackson provider sans JAXB. De plus, vous pouvez toujours utiliser la dernière version de jackson en downlaoding jackson-all-X. Y. z-jar de sa toile.
cette méthode ne va pas interférer avec vos annotations jaxb donc je suggère d'essayer!
depuis Jersey est une mise en œuvre de référence de JAX-RS et JAX-RS est entièrement axé sur la fourniture d'une façon standard de mettre en œuvre le point final pour le service de repos, les questions de sérialisation de la charge utile est laissée à d'autres normes.
je pense que si ils inclus sérialisation d'un objet dans le JAX-RS standard, il devient rapidement un grand multi-tête de la bête qui serait difficile à mettre en œuvre et de perdre un peu de son orientation.
j'apprécie à quel point Jersey est sur la livraison propre et simple d'utiliser des points de repos. Dans mon cas, j'ai juste sous-classé un parent qui a toute la plomberie JAXB dedans donc le fait de regrouper des objets entre binaire et XML est très propre.
avec un bootstrapping spécifique au petit 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 paquet, mais cela va causer des problèmes si vous êtes à l'aide de Soudure pour le test):
<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>
A partir de là, vous pouvez créer une classe annotée JAXB. Voici un exemple:
@XmlRootElement
public class TextMessage {
private String text;
public String getText() { return text; }
public void setText(String s) { this.text = text; }
}
alors vous pouvez créer le test d'unité 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'il garde 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 dans la norme et nous ne l'utilisons pas réellement. aussi être plus lent que GSON, et beaucoup plus compliqué que nécessaire.
je comprends les vues XML mais il aurait montré une certaine prévoyance pour exiger le soutien de JSON POJOs comme équipement standard. Il n'y a pas de sens à recoder les identificateurs JSON avec des caractères spéciaux si votre implémentation est JSON et votre client est un RIA JavaScript.
en outre, pas que les haricots Java ne sont pas des POJOs. Je voudrais utiliser quelque chose comme ceci sur la surface extérieure 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.