Cómo serializar un POJO en parámetros de consulta con Jersey

He estado jugando y creando múltiples bibliotecas pequeñas de clientes RESTful de Java para diferentes servicios en mi empresa. La mayoría de las veces, no puedo cambiar nada en el lado del servidor y necesito escribir los fragmentos de código de Jersey para interactuar con las API RESTful existentes.


Contexto

Hasta ahora, he estado usando Jersey con Jackson para usar JSON: cuando pregunto en un POJO, lo deserializo desde JSON y cuando necesito enviar un POJO, lo serializo en un cuerpo JSON. Estos dos tipos de fragmentos han estado haciendo el trabajo para mí hasta ahora …

Consulta y des-serialización.

ClientResponse response = webResource .path("/path/to/resource") .queryParam("key", "value") .accept(Mediatype.APPLICATION_JSON) .get(ClientResponse.class); // (...) Check response status code MyClassPojo pojo = response.getEntity(MyClassPojo.class); 

Serialización y envío.

 ClientResponse response = webResource .path("/path/to/resource") .type(Mediatype.APPLICATION_JSON_TYPE) .accept(Mediatype.APPLICATION_JSON) .post(ClientResponse.class, pojo) // (...) Check response status code 

Problema

Ahora estoy frente a un servidor RESTful que no acepta cuerpos JSON para enviar mis POJOs. Lo único que parece funcionar es usar parámetros de consulta.

Por ejemplo, si quiero enviar el objeto.

 public MyClassPojo { public int attr1; public String attr2; } MyClassPojo pojo = new MyClassPojo(); pojo.attr1 = 42; pojo.attr2 = "Foo bar"; 

Me hubiera encantado serializarlo en JSON:

 { "attr1": 42, "attr2": "Foo bar" } 

Pero este servidor RESTful específico está esperando parámetros de consulta:

 ?attr1=42&attr2=Foo+bar 

Pregunta

Esto es un poco horrible, pero realmente no tengo otra opción … y ahora espero que haya una manera fácil de lograr esto con Jersey: ¿cómo puedo serializar objetos automáticamente como parámetros de consulta, para enviarlos a un servidor RESTful?

Nota: cerré esta pregunta como @Jukka respondió. No dudes en referirte a una nueva pregunta que crees, si como yo, en realidad estás buscando una manera de enviar datos de ur-codificados con x-www-form. Estoy a punto de hacer que algo funcione …


Actualizar

Siguiendo una idea de @Jukka escribí el siguiente método:

 public MultivaluedMap toQueryParams() { final MultivaluedMap queryParams = new Form(); final Field[] fields = getClass().getDeclaredFields(); for (Field field : fields) { final boolean accessible = field.isAccessible(); try { field.setAccessible(true); final Object value = field.get(this); if (value != null) { final String name = field.getName(); queryParams.add(name, value.toString()); } } catch (IllegalAccessException e) { LOGGER.error("Error accessing a field", e); } finally { field.setAccessible(accessible); } } return queryParams; } 

Este es un excelente punto de partida y funcionaría perfectamente si realmente necesita parámetros de consulta. En mi caso, me confundí y en realidad necesito un x-www-form-urlencoded. Para eso tuve que escribir un MessageBodyWriter!


Mi proveedor de encoding de formularios

 @Produces(MediaType.APPLICATION_FORM_URLENCODED) public class MyFormEncodingProvider implements MessageBodyWriter { private static final String ENCODING = "UTF-8"; @Override public boolean isWriteable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { return true; } @Override public long getSize(Object obj, Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { return -1; } @Override public void writeTo(Object obj, Class aClass, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap stringObjectMultivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException { final Writer osWriter = new OutputStreamWriter(outputStream); final MultivaluedMap fieldsAndValues = getFieldsAndValues(obj); boolean firstVal = true; for (Entry<String, List> entry : fieldsAndValues.entrySet()) { final List values = entry.getValue(); if (values == null || values.size() == 0) { if (!firstVal) { osWriter.write("&"); } osWriter.write(entry.getKey() + "="); firstVal = false; } else { for (String value : values) { if (!firstVal) { osWriter.write("&"); } osWriter.write(entry.getKey() + "=" + URLEncoder.encode(value, ENCODING)); firstVal = false; } } } osWriter.flush(); osWriter.close(); } private static MultivaluedMap getFieldsAndValues(Object obj) { // Find all available fields final Collection allFields = new ArrayList(); Class clazz = obj.getClass(); while (clazz != null && clazz != Object.class) { Collections.addAll(allFields, clazz.getDeclaredFields()); clazz = clazz.getSuperclass(); } // Get all non-null values final MultivaluedMap queryParams = new Form(); for (Field field : allFields) { final boolean accessible = field.isAccessible(); try { field.setAccessible(true); final Object value = field.get(obj); if (value != null) { final String name = field.getName(); queryParams.add(name, value.toString()); } } catch (IllegalAccessException e) { Logger.getLogger(AbstractIMSPojo.class).error("Error accessing a field", e); } finally { field.setAccessible(accessible); } } return queryParams; } } 

Simplemente implementaría este tipo de vista a su POJO:

 class Pojo { ... public MultiValuedMap asQueryParams() { ... } } 

y pasar el resultado a WebResource.queryParams(..) .

Intereting Posts