55 votes

Comment personnaliser la sérialisation d'une liste d'objets JAXB en JSON?

Je suis l'aide de Jersey pour créer un service web REST pour un composant serveur.

Le JAXB-annoté objet que je veux pour sérialiser dans une liste ressemble à ceci:

@XmlRootElement(name = "distribution")
@XmlType(name = "tDistribution", propOrder = {
    "id", "name"
})
public class XMLDistribution {
    private String id;
    private String name;
    // no-args constructor, getters, setters, etc
}

J'ai un RESTE de ressources pour récupérer une distribution qui ressemble à ceci:

@Path("/distribution/{id: [1-9][0-9]*}")
public class RESTDistribution {
    @GET
    @Produces("application/json")
    public XMLDistribution retrieve(@PathParam("id") String id) {
        return retrieveDistribution(Long.parseLong(id));
    }
    // business logic (retrieveDistribution(long))
}

J'ai aussi un RESTE de ressources pour récupérer une liste de toutes les distributions, qui ressemble à ceci:

@Path("/distributions")
public class RESTDistributions {
    @GET
    @Produces("application/json")
    public List<XMLDistribution> retrieveAll() {
        return retrieveDistributions();
    }
    // business logic (retrieveDistributions())
}

J'utilise un ContextResolver pour personnaliser JAXB la sérialisation, qui est actuellement configuré comme ceci:

@Provider
@Produces("application/json")
public class JAXBJSONContextResolver implements ContextResolver<JAXBContext> {
    private JAXBContext context;
    public JAXBJSONContextResolver() throws Exception {
        JSONConfiguration.MappedBuilder b = JSONConfiguration.mapped();
        b.nonStrings("id");
        b.rootUnwrapping(true);
        b.arrays("distribution");
        context = new JSONJAXBContext(b.build(), XMLDistribution.class);
    }
    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return context;
    }
}

Les deux RESTE des ressources de travail, ainsi que le contexte de résolution. Ceci est un exemple de sortie pour le premier:

// path: /distribution/1
{"id":1,"name":"Example Distribution"}

Ce qui est exactement ce que je veux. Ceci est un exemple de sortie de la liste:

// path: /distributions
{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}

Ce qui n'est pas tout à fait ce que je veux.

Je ne comprends pas pourquoi il y a un enfermant distribution balise. J'ai voulu l'enlever avec .rootUnwrapping(true) dans le contexte de résolution, mais apparemment ce ne supprime une autre balise englobante. C'est la sortie avec .rootUnwrapping(false):

// path: /distribution/1
{"distribution":{"id":1,"name":"Example Distribution"}} // not ok
// path: /distributions
{"xMLDistributions":{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}}

J'ai également eu à configurer .arrays("distribution") de toujours obtenir un tableau JSON, même avec un seul élément.

Idéalement, j'aimerais avoir cette sortie:

// path: /distribution/1
{"id":1,"name":"Example Distribution"} // currently works
// path: /distributions
[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]

J'ai essayé de retourner un List<XMLDistribution>, XMLDistributionList (wrapper autour d'une liste), un XMLDistribution[], mais je ne pouvais pas trouver un moyen d'obtenir un simple tableau JSON de distributions dans mon format requis.

J'ai aussi essayé les autres notations retourné par JSONConfiguration.natural(), JSONConfiguration.mappedJettison(), etc, et ne pouvait pas obtenir quelque chose ressemblant à ce que j'ai besoin.

Personne ne sait si il est possible de configurer JAXB pour ce faire?

104voto

Jonathan Points 1406

J'ai trouvé une solution: remplacer le JAXB sérialiseur JSON avec un meilleur comportement sérialiseur JSON comme Jackson. Le plus simple est d'utiliser jackson-jaxrs, qui a déjà fait pour vous. La classe est JacksonJsonProvider. Tout ce que vous avez à faire est de modifier votre projet web.xml de sorte que Jersey (ou un autre JAX-RS de la mise en œuvre des analyses. Voici ce que vous devez ajouter:

<init-param>
  <param-name>com.sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;org.codehaus.jackson.jaxrs</param-value>
</init-param>

Et c'est tout là est à lui. Jackson sera utilisé pour la sérialisation JSON, et il fonctionne de la même façon que vous attendez pour les listes et les tableaux.

Plus la méthode est d'écrire votre propre MessageBodyWriter enregistré à produire "application/json". Voici un exemple:

@Fournisseur
@Produit("application/json")
public class JsonMessageBodyWriter implémente MessageBodyWriter {
@Override
 publics à long getSize(Object obj, Classe, type genericType,
 Annotation[] annotations, MediaType mediaType) {
 return -1;
}

@Override
 public boolean isWriteable(Classe, type genericType,
 Annotation annotations[], MediaType mediaType) {
 return true;
}

@Override
 public void writeTo(Objet cible, le type de Classe, de Type genericType,
 Annotation[] annotations, MediaType mediaType,
 MultivaluedMap httpHeaders, OutputStream outputStream)
 throws IOException { 
 nouveau ObjectMapper().writeValue(outputStream, cible);
}
}

Vous devrez vous assurer que votre web.xml comprend le paquet, comme pour le prêt-à-solution ci-dessus.

De la manière suivante: le tour est joué! Vous verrez correctement formé JSON.

Vous pouvez télécharger Jackson à partir d'ici: http://jackson.codehaus.org/

13voto

Gabriele Points 71

La réponse de Jonhatan est excellente et elle m'a été très utile.

Juste une mise à jour:

Si vous utilisez la version 2.x de Jackson (par exemple la version 2.1), la classe est com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider. Par conséquent, le fichier web.xml est:

 <init-param>
  <param-name>com.sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;com.fasterxml.jackson.jaxrs.json</param-value>
</init-param>
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X