2010-04-13 17 views
8

En mi aplicación utilizo Jersey REST para serializar objetos complejos. Esto funciona bastante bien. Pero hay algunos métodos que simplemente devuelven un int o boolean.Cómo serializar primitivas de Java utilizando Jersey REST

Jersey no puede manejar tipos primitivos (que yo sepa), probablemente porque no están anotados y Jersey no tiene ninguna 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 de contenedor?

+3

JAX-RS/Jersey no es compatible con la serialización de tipos primitivos o incluso los tipos de envoltura tales como Integer, Boolean etc. Que yo sepa, el enfoque que ha tomado parece ser única manera. –

+0

Bien, ¡gracias! – Olvagor

Respuesta

3

¿Está escribiendo un servicio o un cliente? En el final del servicio, simplemente escriba 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. Adding support for new representations

2

En realidad lo mejor es escribir un proveedor de ContextResolver personalizada como la siguiente que utiliza natural construcción de JSON.

@Provider 
    public class YourContextResolver implements ContextResolver<JAXBContext> { 

    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; 
    } 
} 

La única cosa especial aquí a notar es el YourSpecialBean.class en la Clase []. Esto define una matriz de tipos de clase que este proveedor resolverá naturalmente.

2

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.

Mejor programador podría implementar helper para iterar archivos .class y enumerar las clases apropiadas de forma automática identificando etiquetas @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):

<servlet> 
    <servlet-name>RESTServlet</servlet-name> 
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> 
    <init-param> 
    <param-name>javax.ws.rs.Application</param-name> 
    <param-value>com.myapp.rest.RESTApplication</param-value> 
    </init-param> 
</servlet> 
<servlet-mapping> 
    <servlet-name>RESTServlet</servlet-name> 
    <url-pattern>/servlet/rest/*</url-pattern> 
</servlet-mapping> 

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<JAXBContext> { 
    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<Class<?>> getClasses() { 
     // list JAXB resource/provider/resolver classes 
     Set<Class<?>> classes = new HashSet<Class<?>>(); 
     //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; 
    } 
} 

Clases MyBean1, MyBean2 son objetos Java simple y clase MyBeansResource es el que tiene @Path funciones de descanso. No hay nada especial en ellos que esperen jaxp @Annotations estándar aquí y allá.Después de esta jerga java documentos JSON tienen

  • cero o matrices de lista de un solo elemento siempre se escriben como matriz JSON ([] campo)
  • enteros primitivos y campos booleanos se escriben como primitivas JSON (sin las comillas)

uso el siguiente entorno

  • Sun Java JDK1.6.x
  • Apache Tomcat 6.x
  • bibliotecas Jersey v1.14 (jersey-archive-1.14.zip)
  • webapps/miaplicacion carpeta WEB-INF// lib tiene asm-3.3.1.jar, Jackson-core-asl.jar, jersey-cliente .jar, jersey-core.jar, jersey-json.jar, jersey-server.jar, bibliotecas jersey-servlet.jar
  • añadir opcional anotación detector.jar si utiliza herramienta de descubrimiento infomas-ASL

jersey-archive.zip tenía un archivo asm-3.1.jar anterior, probablemente funciona bien, pero chapter_deps.html enlaza con 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, solo 15KB). 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

4

Tenga una mirada en Genson .Se me ha ayudado mucho con un similares problem.With Genson podría utilizar genéricos como int, boolean, listas y así sucesivamente ... Aquí está un ejemplo rápido.

@GET 
@Produces(MediaType.APPLICATION_JSON) 
public Response getMagicList() { 
    List<Object> 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 se puede recuperar muy simple como esto:

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()); 
    } 

Usted verá que Genson ayudará a descifrar el JSON en el cliente también y la salida de la clase correcta para cada.

+0

Corregir el enlace html al proyecto Genson es http://code.google.com/p/genson/ – Whome

2

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, que está escrito por el mismo autor del servidor de la mayor parte de las veces, se puede envolver un valor de retorno tales cuerda y volver a convertirlo en int. Los clientes escritos en otros idiomas deben conocer los tipos de devolución de cualquier forma.

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

O tal vez me falta algo importante aquí?

+1

esto definitivamente no debería ser tan engorroso e incluido en la clase jersey en mi opinión. Hice la misma conclusión el domingo a las 10 p.m. trataré con la maldita cuerda, ja. – JesseBoyd

2

que tenían el mismo problema hoy en día y no se rindieron hasta que encontré una muy buena solución adecuada. 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. listas vacías se representan como [] (casi imposible) Listas
  2. un elemento se representan como [] (configuración de asignación difícil pero solamente)
  3. Muchas listas de elementos se representan como [] (fácil)

Empieza de fácil a imposible.

3) nada hoy en día normal de Mapeo JSON

2) Registro JAXBContextResolver como la siguiente

@Provider 
public class JAXBContextResolver implements ContextResolver<JAXBContext> { 
    private final JAXBContext context; 
    private final Set<Class<?>> types; 
    private Class<?>[] ctypes = { Pojo.class }; //your pojo class 
    public JAXBContextResolver() throws Exception { 
     this.types = new HashSet<Class<?>>(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 método sólo funciona para la clase Colecciones $ EmptyList. Que encuentres una manera de hacerlo general para todas las colecciones, están vacías. Puede programar el código con EmptyList por lo tanto.

@Provider 
@Produces(value={MediaType.APPLICATION_JSON}) 
public class EmptyListWriter implements MessageBodyWriter<AbstractList> { 

    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<String, Object> headers, OutputStream outputStream) throws IOException, WebApplicationException { 
     if (list.isEmpty()) 
      outputStream.write(EMPTY_JSON_ARRAY.getBytes());    
    } 
} 
Cuestiones relacionadas