Cómo serializar primitivos de Java utilizando Jersey REST

En mi aplicación uso Jersey REST para serializar objetos complejos. Esto funciona bastante bien. Pero hay algunos métodos que simplemente devuelven un int o boolean.

Jersey no puede manejar tipos primitivos (que yo sepa), probablemente porque no están anotados y Jersey no tiene una anotación predeterminada para ellos. Trabajé en eso creando tipos complejos como RestBoolean o RestInteger, que simplemente tienen un valor int o boolean y tienen las anotaciones apropiadas.

¿No hay una manera más fácil que escribir estos objetos contenedores?

Eche un vistazo a Genson . Me ayudó mucho con un problema similar. Con Genson podría usar generics como int, boolean, listas, etc. Este es un ejemplo rápido.

@GET @Produces(MediaType.APPLICATION_JSON) public Response getMagicList() { List objList = new ArrayList<>(); stringList.add("Random String"); stringList.add(121); //int stringList.add(1.22); //double stringList.add(false); //bolean return Response.status(Status.OK).entity(objList).build(); } 

Esto producirá una bruja JSON válida que se puede recuperar de esta forma muy simple:

  Client client = Client.create(); WebResource webResource = client.resource("...path to resource..."); List objList = webResource.accept(MediaType.APPLICATION_JSON).get(ArrayList.class); for (Object obj : objList) { System.out.println(obj.getClass()); } 

Verás que Genson te ayudará a decodificar el JSON en el lado del cliente también y mostrará la clase correcta para cada uno.

¿Estás escribiendo un servicio o un cliente? Al final del servicio, simplemente escribiría un MessageBodyWriter para serializar una secuencia de datos a un objeto Java para sus tipos. En mis casos de uso, los servicios que estoy escribiendo salen a JSON o XML, y en el caso de XML, simplemente lanzo una anotación JAXB en la parte superior de mis clases y listo.

¿Has mirado la guía de usuario de Jersey con respecto a esto?

3.6. Agregar soporte para nuevas representaciones

En realidad, la mejor opción es escribir un Proveedor ContextResolver personalizado como el siguiente que usa la construcción natural de JSON.

  @Provider public class YourContextResolver implements ContextResolver { private JAXBContext context; private Class< ?>[] types = { YourSpecialBean.class }; public YourContextResolver() throws Exception { this.context = new JSONJAXBContext( JSONConfiguration.natural().build(), types); } public JAXBContext getContext(Class< ?> objectType) { for (int i = 0; i < this.types.length; i++) if (this.types[i].equals(objectType)) return context; return null; } } 

Lo único especial aquí para notar es la clase YourSpecialBean.class en la clase []. Esto define una matriz de tipos de clase que este proveedor resolverá naturalmente.

Tell Jersey genera documentos JSON adecuados (json natural). Utilizo la misma clase para la aplicación de reposo y JAXBContext resolver, la encontré la encapsulación más limpia.

Un mejor progtwigdor podría implementar helper para iterar archivos .class y enumerar las clases apropiadas de forma automática al identificar las tags @Annotation. No sé cómo hacerlo en tiempo de ejecución en un código fuente propio.

Estos dos enlaces fueron útiles para estudiar esta jerga adicional de Java. No sé por qué no hay un parámetro Jersey para hacer que todo salga bien de la caja.

WEB-INF / web.xml (fragmento):

  RESTServlet com.sun.jersey.spi.container.servlet.ServletContainer  javax.ws.rs.Application com.myapp.rest.RESTApplication    RESTServlet /servlet/rest/*  

com.myapp.rest.RESTApplication.java

 package com.myapp.rest; import java.util.*; import javax.ws.rs.core.Application; import javax.ws.rs.ext.ContextResolver; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import com.sun.jersey.api.json.JSONConfiguration; import com.sun.jersey.api.json.JSONJAXBContext; public class RESTApplication extends Application implements ContextResolver { private JAXBContext context; private Class< ?>[] types; public RESTApplication() throws JAXBException { // list JAXB bean types to be used for REST serialization types = new Class[] { com.myapp.rest.MyBean1.class, com.myapp.rest.MyBean2.class, }; context = new JSONJAXBContext(JSONConfiguration.natural().build(), types); } @Override public Set> getClasses() { // list JAXB resource/provider/resolver classes Set> classes = new HashSet>(); //for(Class< ?> type : types) // classes.add(type); classes.add(MyBeansResource.class); classes.add(this.getClass()); // used as a ContextResolver class return classes; } @Override public JAXBContext getContext(Class< ?> objectType) { // this is called each time when rest path was called by remote client for (Class< ?> type : types) { if (type==objectType) return context; } return null; } } 

Las clases MyBean1, MyBean2 son objetos simples de Java y la clase MyBeansResource es la que tiene las funciones @Path rest. No hay nada especial en ellos que esperen jaxp @Annotations estándar aquí y allá. Después de esta jerga java, los documentos JSON tienen

  • Los arrays de listas de cero o un solo elemento siempre se escriben como json array ([] field)
  • los enteros primitivos y los campos booleanos se escriben como json primitivos (sin citas)

Uso el siguiente entorno

  • Sun Java JDK1.6.x
  • Apache Tomcat 6.x
  • Bibliotecas de Jersey v1.14 (jersey-archive-1.14.zip)
  • La carpeta webapps / myapp / WEB-INF / lib tiene asm-3.3.1.jar, jackson-core-asl.jar, jersey-client.jar, jersey-core.jar, jersey-json.jar, jersey-server.jar , bibliotecas jersey-servlet.jar
  • agrega opcional anotación-detector.jar si usa la herramienta de descubrimiento infomas-asl

jersey-archive.zip tenía un archivo asm-3.1.jar antiguo, probablemente funciona bien, pero chapter_deps.html lo vincula a un archivo más reciente. Ver la lista de enlaces en la parte superior.

Editar Encontré una excelente herramienta de descubrimiento de anotaciones (rápida, ligera y de tan solo 15 KB). Consulte esta publicación sobre cómo puedo autodetectar tipos en tiempo de ejecución y ya no necesito editar la aplicación RESTA cada vez que se agrega un nuevo java (jaxb).

https://github.com/rmuller/infomas-asl/issues/7

Tuve el mismo problema hoy y no me rendí hasta que encontré una solución realmente buena. No puedo actualizar la biblioteca jersey de 1.1.5 es un sistema heredado. My Rest Service devuelve una lista y deben seguir esas reglas.

  1. Las listas vacías se muestran como [] (casi imposible)
  2. Las listas de elementos se representan como [] (configuración de asignación difícil pero solo)
  3. Muchas listas de elementos se representan como [] (fácil)

Comienza de fácil a imposible.

3) nada hoy en día Normal JSON Mapping

2) Registre JAXBContextResolver como el siguiente

 @Provider public class JAXBContextResolver implements ContextResolver { private final JAXBContext context; private final Set> types; private Class< ?>[] ctypes = { Pojo.class }; //your pojo class public JAXBContextResolver() throws Exception { this.types = new HashSet>(Arrays.asList(ctypes)); this.context = new JSONJAXBContext(JSONConfiguration.mapped() .rootUnwrapping(true) .arrays("propertyName") //that should rendered as JSONArray even if the List only contain one element but doesn't handle the empty Collection case .build() , ctypes); } @Override public JAXBContext getContext(Class< ?> objectType) { return (types.contains(objectType)) ? context : null; } } 

1) El siguiente enfoque solo funciona para la clase Collections $ EmptyList. Que encuentres una manera de hacerlo general para todas las colecciones, están vacías. Puede progtwigr el código con EmptyList por lo tanto.

 @Provider @Produces(value={MediaType.APPLICATION_JSON}) public class EmptyListWriter implements MessageBodyWriter { private static final String EMPTY_JSON_ARRAY = "[]"; @Override public long getSize(AbstractList list, Class< ?> clazz, Type type, Annotation[] annotations, MediaType mediaType) { return EMPTY_JSON_ARRAY.length(); } @Override public boolean isWriteable(Class< ?> clazz, Type type, Annotation[] annotations, MediaType mediaType) { return clazz.getName().equals("java.util.Collections$EmptyList"); } @Override public void writeTo(AbstractList list, Class< ?> clazz, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap headers, OutputStream outputStream) throws IOException, WebApplicationException { if (list.isEmpty()) outputStream.write(EMPTY_JSON_ARRAY.getBytes()); } } 

Acabo de descubrir que devolver un tipo primitivo con Jersey es problemático. Decidí devolver String en su lugar. Quizás esto no esté limpio, pero no creo que esté demasiado sucio. El cliente Java, escrito por el mismo autor del servidor la mayoría de las veces, puede ajustar dicho valor de retorno de cadena y convertirlo de nuevo a int. Los clientes escritos en otros idiomas deben conocer los tipos de devolución de cualquier forma.

Definir RestInteger, RestBoolean puede ser otra opción, sin embargo, es más engorroso y veo muy poca ventaja para ser atractivo.

O tal vez me estoy perdiendo algo importante aquí?