Jackson JSON – Deserializar un array en una petición REST

En un servicio REST (jax-rs) puedes enviar los parámetros de una petición de varias formas: en la URL (@QueryParam), en el cuerpo del mensaje con el encabezado “application/x-www-form-urlencoded” (@FormParam), como parte de la URL (@PathParam).

Pero que pasa si el cliente envía toda la información en el cuerpo del mensaje en formato JSON.
Por ejemplo:

    {
        "tipo":"URGENTE",
        "actores":[
        {
            "nombre":"Juan",
            "apellidos":"Peréz",
            "idEstado":null,
            "idMunicipio":null,
            "idColonia":null,
            "calle":"Calle 2",
            "numero":"155",
            "codigoPostal":"76800"
        }
        ],
        "demandados":[
        {
            "nombre":"María",
            "apellidos":"López",
            "idEstado":null,
            "idMunicipio":null,
            "idColonia":null,
            "calle":"Calle 4",
            "numero":"45",
            "codigoPostal":"76800"
        }
        ]
    }

Podríamos crear una clase que reflejará esta estructura, pero si solo nos interesa algún campo o la petición no tiene nada que ver con nuestro modelo, no parece la mejor opción.

En este caso, en nuestro modelo existiría una clase Actor y Demandado que comparten los mismos campos, por lo que podríamos usar la herencia.

    public class Parte {
        String id;
        String nombre;
        String apellidos;
        String idEstado;
        String idMunicipio;
        String idColonia;
        String calle;
        String numero;
        String codigoPostal;
        public Parte() {
        }
        //... getter's y setter's, métodos, etc.
    }
    public class Actor extends Parte{
        //... otras propiedades
        public Actor() {
        }
        //... otros métodos
    }
    public class Demandado extends Parte{
        //... otras propiedades
        public Actor() {
        }
        //... otros métodos
    }

en nuestro servicio REST (Apache CXF) podríamos deserializar el arreglo de Actores de la siguiente forma

    @Path("solicitar/")
    public String solicitar(String data){
        String response = null;
        try{
            ObjectMapper mapper=new ObjectMapper();
            mapper.configure(SerializationConfig.Feature.WRITE<em>DATES</em>AS<em>TIMESTAMPS, false);
            mapper.configure(Feature.FAIL</em>ON<em>UNKNOWN</em>PROPERTIES, false);
            Map<String, Object> map = mapper.readValue(data, new TypeReference<Map<String, Object>>(){});
            String tipo = (String) map.get("tipo");
            List
<Parte> actores = mapper.convertValue(map.get("actores"), new TypeReference
<List<Parte>>() {});
            //... registrar la solicitud
            response = mapper.writeValueAsString(actores);
        }catch(Exception e){
            String error = "Ocurrió un problema al hacer la solicitud";
            log.error(error, e);
            response = String.format("{\"response\":{\"status\": -1, \"data\": \"%s\"}}", error);
        }
        return response;
    }

Fuentes:
http://wiki.fasterxml.com/JacksonDataBinding
http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-DealingwithParameters

Anuncios

GWT – JSON dates

JSON no tiene un tipo de dato Date, así que cuando transfieres fechas mediante JSON debes convenir un formato de fecha -por ejemplo, yyyy-MM-dd’T’hh:mm:ss.SZ- Un ejemplo de fecha en este formato sería “2013-03-08T16:23:35.000+0000”.

Para facilitar un poco las cosas, decidí utilizar un Overlay Type para la representación de mi objeto y acceder a sus propiedades mediante getter’s y setter’s. Estos métodos realizan la conversión de cadena a fecha, y viceversa, con la ayuda de la clase DateTimeFormat.

    public class Audiencia extends JavaScriptObject {</p><pre><code>    private final static DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat("yyyy-MM-dd'T'hh:mm:ss.SZ");

    public final native int getId()/*-{
        return this.id;
    }-*/;

    public final Date getInicio(){
        return dateTimeFormat.parseStrict(getInicioNative());
    };

    private final native String getInicioNative()/*-{
        return this.inicio;
    }-*/;

    public final Date getFin(){
        return dateTimeFormat.parseStrict(getFinNative());
    };

    private final native String getFinNative()/*-{
        return this.fin;
    }-*/;

    protected Audiencia() {
    }

    public static void getAudiencias(String idSala,
            final AsyncCallback&lt;Audiencia[]&gt; callback) {
        try {
            String queryParams = "?sala="+URL.encodeQueryString(idSala);
            RequestBuilder rb = new RequestBuilder(RequestBuilder.GET,
                    Consts.REST_AUDIENCIAS + queryParams);
            rb.sendRequest(null, new RequestCallback() {

                @Override
                public void onResponseReceived(Request request,
                        Response response) {
                    if(response.getStatusCode() == 200){
                        JavaScriptObject jso = JSON.decode(response.getText());
                        DSResponse ds = new DSResponse(JSOHelper.getAttributeAsJavaScriptObject(jso, "response"));
                        if(ds.getStatus() == 0){
                            Record[] records = ds.getData();
                            Audiencia[] audiencias = new Audiencia[records.length];
                            for(int i=0;i&lt;records.length;i++){
                                audiencias[i] = (Audiencia) records[i].getJsObj();
                            }
                            callback.onSuccess(audiencias);
                        }else{
                            callback.onFailure(new Exception(ds.getAttributeAsString("data")));
                        }
                    }else{
                        callback.onFailure(new Exception(response.getStatusText()));
                    }
                }

                @Override
                public void onError(Request request, Throwable exception) {
                    callback.onFailure(exception);
                }
            });
        } catch (Exception e) {
            callback.onFailure(e);
        }
    }

}
</code></pre><p>

Esto es necesario porque el método JSON.decode) crea el objeto Javascript con propiedades (inicio y fin) de tipo String.

Java XML Serialization vs .NET XML Serialization

Estoy haciendo un programa que genera reportes a partir de un archivo XML. La idea es que cuando se quiera un nuevo reporte o modificar alguno existente solo se tenga que cambiar la definicion del reporte en un archivo XML y no tener que modificar el codigo. Las clases que utilizo son las siguientes:

Esto lo hice en C# de forma sencilla mediante el uso de la clase System.Xml.Serialization.XmlSerializer y sus métodos Serialize y Deserialize. Esta clase me generó un XML como el siguiente:

<?xml version=”1.0″ encoding=”utf-8″?>
<Reporte xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:xsd=”http://www.w3.org/2001/XMLSchema”&gt;
<Nombre>Reporte de Prueba</Nombre>
<Consultas>
<Consulta>
<Nombre>Consulta 0</Nombre>
<SQL>SELECT * FROM CONSULTA_0</SQL>
</Consulta>
<Consulta>
<Nombre>Consulta 1</Nombre>
<SQL>SELECT * FROM CONSULTA_1</SQL>
</Consulta>
</Consultas>
</Reporte>

a mi parecer esta bastante limpio y se entiende lo suficiente para que una persona lo pueda modificar en cualquier editor de texto.
Luego quise hacer lo mismo en java y encontre que lo podia hacer mediante la clase java.beans.XMLEncoder y java.beans.XMLDecoder, y asi fue. El problema es que me genera XML como el siguiente:

<?xml version=”1.0″ encoding=”UTF-8″?>
<java version=”1.5.0_06″ class=”java.beans.XMLDecoder”>
<object class=”tsj.estadisticas.Reporte”>
<void property=”consultas”>
<void method=”add”>
<object class=”tsj.estadisticas.Consulta”>
<void property=”nombre”>
<string>Consulta 0</string>
</void>
<void property=”sql”>
<string>SELECT * FROM CONSULTA_0</string>
</void>
</object>
</void>
<void method=”add”>
<object class=”tsj.estadisticas.Consulta”>
<void property=”nombre”>
<string>Consulta 1</string>
</void>
<void property=”sql”>
<string>SELECT * FROM CONSULTA_1</string>
</void>
</object>
</void>
<void method=”add”>
<object class=”tsj.estadisticas.Consulta”>
<void property=”nombre”>
<string>Consulta 2</string>
</void>
<void property=”sql”>
<string>SELECT * FROM CONSULTA_2</string>
</void>
</object>
</void>
</void>
<void property=”nombre”>
<string>Nuevo Reporte</string>
</void>
</object>
</java>

Y pues no me gustó para nada, así que busqué otras alternativas.
Buscando en google encontré las 3 opciones que me parecieron mejores: JAXB, Simple y JiBX. Escogí Simple por simple… 😀 jeje … que es lo mismo a fácil de aprender, fácil de usar y eficiente, además de que en escencia es similar a la serializacion de C#.
Simple me produjo el siguiente XML:

<reporte>
<nombre>Nuevo Reporte</nombre>
<consultas class=”java.util.ArrayList”>
<consulta>
<nombre>Consulta 0</nombre>
<sql>SELECT * FROM CONSULTA_0</sql>
</consulta>
<consulta>
<nombre>Consulta 1</nombre>
<sql>SELECT * FROM CONSULTA_1</sql>
</consulta>
<consulta>
<nombre>Consulta 2</nombre>
<sql>SELECT * FROM CONSULTA_2</sql>
</consulta>
</consultas>
</reporte>

que es el mismo que en C# excepto por algunas líneas del encabezado.
El único problema que tuve para poder utilizar Simple fue por no haber leído el siguiente texto en la sección de descarga:

For Java 6 the framework has no external
dependancies and is completely self contained. For Java 5 two external JAR files are required,
the StAX API and the StAX implementation, both are provided with the download.

Como la version de java que utilizo es la 1.5, necesitaba tambien agregar las librerias indicadas. Estas librerias se encuentran en la carpeta “lib” donde hayas descomprimido Simple.
Si no las agregas te genera el siguiente error:

Exception in thread “main” java.lang.NoClassDefFoundError: javax/xml/stream/XMLInputFactory