recorrer - ¿Cómo puedo personalizar la serialización de una lista de objetos JAXB a JSON?
recorrer json c# (2)
Estoy usando Jersey para crear un servicio web REST para un componente de servidor.
El objeto anotado JAXB que deseo serializar en una lista se ve así:
@XmlRootElement(name = "distribution")
@XmlType(name = "tDistribution", propOrder = {
"id", "name"
})
public class XMLDistribution {
private String id;
private String name;
// no-args constructor, getters, setters, etc
}
Tengo un recurso REST para recuperar una distribución que se ve así:
@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))
}
También tengo un recurso REST para recuperar una lista de todas las distribuciones, que se ve así:
@Path("/distributions")
public class RESTDistributions {
@GET
@Produces("application/json")
public List<XMLDistribution> retrieveAll() {
return retrieveDistributions();
}
// business logic (retrieveDistributions())
}
Utilizo un ContextResolver para personalizar la serialización de JAXB, que actualmente está configurado así:
@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;
}
}
Ambos recursos REST funcionan, al igual que el solucionador de contexto. Este es un ejemplo de salida para el primero:
// path: /distribution/1
{"id":1,"name":"Example Distribution"}
Que es exactamente lo que quiero Este es un ejemplo de salida para la lista:
// path: /distributions
{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}
Que no es exactamente lo que quiero.
No entiendo por qué hay una etiqueta de distribution
adjunta allí. Quería eliminarlo con .rootUnwrapping(true)
en el contexto de resolución, pero aparentemente eso solo elimina otra etiqueta adjunta. Este es el resultado con .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"}]}}
También tuve que configurar .arrays("distribution")
para obtener siempre una matriz JSON, incluso con un solo elemento.
Idealmente, me gustaría tener esto como un resultado:
// path: /distribution/1
{"id":1,"name":"Example Distribution"} // currently works
// path: /distributions
[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]
Traté de devolver una List<XMLDistribution>
, una XMLDistributionList
(envoltorio alrededor de una lista), un XMLDistribution[]
, pero no pude encontrar una manera de obtener una matriz JSON simple de distribuciones en mi formato requerido.
También probé las otras notaciones devueltas por JSONConfiguration.natural()
, JSONConfiguration.mappedJettison()
, etc., y no pude obtener nada parecido a lo que necesito.
¿Alguien sabe si es posible configurar JAXB para hacer esto?
Encontré una solución: reemplazar el serializador JAXB JSON con un serializador JSON mejor comportado como Jackson. La forma más fácil es usar jackson-jaxrs, que ya lo ha hecho por usted. La clase es JacksonJsonProvider. Todo lo que tiene que hacer es editar el web.xml de su proyecto para que Jersey (u otra implementación de JAX-RS) lo busque. Esto es lo que necesita agregar:
<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>
Y eso es todo lo que hay que hacer. Jackson se usará para la serialización JSON, y funciona de la manera esperada para listas y matrices.
El camino más largo es escribir su propio MessageBodyWriter personalizado registrado para producir "application / json". Aquí hay un ejemplo:
@Provider @Produces("application/json") public class JsonMessageBodyWriter implements MessageBodyWriter { @Override public long getSize(Object obj, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } @Override public boolean isWriteable(Class type, Type genericType, Annotation annotations[], MediaType mediaType) { return true; } @Override public void writeTo(Object target, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream outputStream) throws IOException { new ObjectMapper().writeValue(outputStream, target); } }
Deberá asegurarse de que su web.xml incluya el paquete, como en el caso de la solución preparada anterior.
De cualquier manera: ¡voila! Verás JSON correctamente formado.
Puede descargar Jackson desde aquí: http://jackson.codehaus.org/
La respuesta de Jonhatan es genial y me ha sido muy útil.
Solo una actualización:
si usa la versión 2.x de Jackson (por ejemplo, la versión 2.1), la clase es com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider, por lo tanto, el web.xml es:
<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>