2009-11-02 22 views
32

He estado buscando en Google mi trasero tratando de averiguar cómo hacer esto: tengo un servicio de REST de Jersey. La solicitud que invoca el servicio REST contiene un objeto JSON. Mi pregunta es, desde la implementación del método Jersey POST, ¿cómo puedo obtener acceso al JSON que está en el cuerpo de la solicitud HTTP?Consumo de objeto JSON en el servicio de Jersey

Cualquier consejo, truco o sugerencia del código de muestra sería muy apreciado.

Gracias ...

--Steve

+0

estoy enfrentando el problema similar. ¿hay alguna solución para usar acceso de bajo nivel a JSONObject? – Jugi

Respuesta

10

no estoy seguro de cómo puede conseguir en la propia cadena JSON, pero que sin duda puede conseguir en los datos que contiene la siguiente manera:

Defina una clase de Java anotada de JAXB (C) que tiene la misma estructura que el objeto JSON que se está pasando en la solicitud.

p. Ej. para un mensaje JSON:

{ 
    "A": "a value", 
    "B": "another value" 
} 

usar algo como:

@XmlAccessorType(XmlAccessType.FIELD) 
public class C 
{ 
    public String A; 
    public String B; 
} 

A continuación, se puede definir un método en su clase de recurso con un parámetro de tipo C. Cuando Jersey invoca el método, el objeto JAXB se creará en base al objeto POSTed JSON.

@Path("/resource") 
public class MyResource 
{ 
    @POST 
    public put(C c) 
    { 
    doSomething(c.A); 
    doSomethingElse(c.B); 
    } 
} 
+2

Desafortunadamente, la cadena JSON básicamente se usa más como un diccionario que como un POJO real. Prefiero no tener que crear un nuevo POJO para los objetos JSON. – Steve

+1

¿Ha mirado usando un MessageBodyReader (http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/ext/MessageBodyReader.html). No lo intenté yo mismo, pero es posible que puedas entrar en ese nivel y convertir la secuencia JSON en un mapa, etc. – Andy

+4

En realidad resulta que es mucho más simple de lo que pensaba. Puedo obtener el cuerpo de la solicitud HTTP como un argumento de cadena, y luego procesarlo usando cualquier biblioteca JSON (actualmente usando Google GSON) para convertir los datos de solicitud en objetos locales. – Steve

0

Presentar/POST la forma/HTTP.POST con un parámetro con el JSON como el valor.

@QueryParam jsonString

desolveJson pública (jsonString)

+1

Pensé en hacer eso. Simplemente no es del modo que me gustaría ir. Hay algunos kits de herramientas que incluyen el JSON completo en el cuerpo real de la solicitud HTTP, y me gustaría seguir ese patrón si es posible. – Steve

6

Esto le da acceso al puesto prima.

@POST 
@Path("/") 
@Consumes("text/plain") 
@Produces(MediaType.APPLICATION_JSON) 
public String processRequset(String pData) { 
    // do some stuff, 
    return someJson; 
} 
+1

Esta fue una manera ingeniosa y fácil de pasar JSON de mi cliente y no tener el servidor/cliente que necesita saber sobre los objetos que estoy pasando. Acabo de usar GSON para serializarlo y adjuntarlo a la publicación. Funcionó muy bien! Gracias. – bh5k

14

Como ya se ha sugerido, el cambio de la @Consumes Content-Type a text/plain va a funcionar, pero no me parece bien desde el punto de vista de la API REST.

Imagine que su cliente tiene que ENVIAR JSON a su API pero necesita especificar el encabezado Content-Type como text/plain. No está limpio en mi opinión. En términos simples, si su API acepta JSON, el encabezado de la solicitud debe especificar Content-Type: application/json.

Para aceptar JSON pero serializarlo en un objeto String en lugar de un POJO, puede implementar un MessageBodyReader personalizado. Hacerlo de esta manera es igual de fácil, y no tendrá que comprometer sus especificaciones de API.

Vale la pena leer los documentos para MessageBodyReader para que sepa exactamente cómo funciona. Esto es cómo lo hice:

Paso 1. Implementar una costumbre MessageBodyReader

@Provider 
@Consumes("application/json") 
public class CustomJsonReader<T> implements MessageBodyReader<T> { 
    @Override 
    public boolean isReadable(Class<?> type, Type genericType, 
     Annotation[] annotations,MediaType mediaType) { 
    return true; 
    } 

    @Override 
    public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, 
     MediaType mediaType, MultivaluedMap<String, String> httpHeaders, 
     InputStream entityStream) throws IOException, WebApplicationException { 

    /* Copy the input stream to String. Do this however you like. 
    * Here I use Commons IOUtils. 
    */ 
    StringWriter writer = new StringWriter(); 
    IOUtils.copy(entityStream, writer, "UTF-8"); 
    String json = writer.toString(); 

    /* if the input stream is expected to be deserialized into a String, 
    * then just cast it 
    */ 
    if (String.class == genericType) 
     return type.cast(json); 

    /* Otherwise, deserialize the JSON into a POJO type. 
    * You can use whatever JSON library you want, here's 
    * a simply example using GSON. 
    */ 
    return new Gson().fromJson(json, genericType); 
    } 
}

El concepto básico de arriba es para comprobar si se espera que el flujo de entrada a convertir en un String (especificado por Type genericType)Si es así, simplemente transfiera el JSON al type especificado (que será un String). Si el tipo esperado es algún tipo de POJO, utilice una biblioteca JSON (por ejemplo, Jackson o GSON) para deserializarlo en un POJO.

Paso 2. vincular a su MessageBodyReader

Esto depende de qué marco que está utilizando. Me parece que Guice y Jersey trabajan bien juntos. Así es como me ato mi MessageBodyReader en Guice:

En mi JerseyServletModule Ato el lector como tal -

bind(CustomJsonReader.class).in(Scopes.SINGLETON);

Lo anterior CustomJsonReader se deserializar cargas útiles JSON en POJOs, así como, si simplemente desea que la prima JSON, String objetos.

El beneficio de hacerlo de esta manera es que aceptará Content-Type: application/json. En otras palabras, el controlador de solicitudes se puede configurar para consumir JSON, lo que parece adecuado:

@POST 
@Path("/stuff") 
@Consumes("application/json") 
public void doStuff(String json) { 
    /* do stuff with the json string */ 
    return; 
}
11

Jersey apoya el acceso de bajo nivel a la JSONObject analizada utilizando los tipos jettison JSONObject y JSONArray.

<dependency> 
    <groupId>org.codehaus.jettison</groupId> 
    <artifactId>jettison</artifactId> 
    <version>1.3.8</version> 
</dependency> 

Por ejemplo:

{ 
    "A": "a value", 
    "B": "another value" 
} 


@POST 
@Path("/") 
@Consumes(MediaType.APPLICATION_JSON) 
public void doStuff(JSONObject json) { 
    /* extract data values using DOM-like API */ 
    String a = json.optString("A"); 
    Strong b = json.optString("B"); 
    return; 
} 

Véase el Jersey documentation para más ejemplos.

+4

Esto no funciona para mí. Obtengo ... 'Campo no reconocido" A "(clase org.json.JSONObject), no marcado como ignorable' ... y si agrego lo siguiente ... ' mapper.configure (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); ' ... Luego el objeto viene vacío' {} ' – crowmagnumb

+0

esto no funciona. estoy obteniendo el error 415 ¿Hay alguna solución y qué dependencia se debe agregar para el acceso json de bajo nivel? – Jugi

Cuestiones relacionadas