GWT – JSON dates

marzo 11, 2013

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.

Anuncios

Para modificar la consulta de un DataSet puedes utilizar la sección “Property Binding”

en mi caso necesitaba reemplazar en la conulta el valor de un parámetro.

Para reemplazar una cadena con otra lo puedes hacer en el “Expression Builder” de la siguiente manera



var q=new Packages.java.lang.String("SELECT COUNT(*) AS TOTAL \
FROM @OFICINA.EMPLEADOS A, @OFICINA.PUESTOS R, \
@OFICINA.SALARIOS D, @OFICINA.DEPTOS O \
WHERE A.NID_ACO_EXP= R.NID_ACO \
R.NID_DMD=D.NID_DMD AND R.NORDEN=O.NID_ORDEN \
AND TRUNC(R.FECHA) BETWEEN :INICIO AND :FIN");
q.replaceAll("@OFICINA",params["OFICINA"].value);

el código es javascript, pero podemos utilizar clases Java también. En este caso utilizamos la clase String de Java en vez de Javascript, porque el método de javascript no reemplaza todas las coincidencias.

Fuentes:
birt exchange

Para no almacenar las contraseñas de los usuarios, puedes guardar el hash y compararlo en el proceso de inicio de sesión. Existen varios algoritmos que te permiten obtener un hash, aquí mostraré cómo utilizar el SHA-256 en java.

String password="secret";
MessageDigest sha256=MessageDigest.getInstance("SHA-256");
sha256.update(password.getBytes("UTF-8"));
byte[] digest = sha256.digest();
StringBuffer sb=new StringBuffer();
for(int i=0;i&lt;digest.length;i++){
    sb.append(String.format("%02x", digest[i]));
}
String hash=sb.toString(); //2bb80d5...527a25b

En internet hay una función que utiliza el método Integer.toHexString pero ésta no imprime el prefijo 0 (cero) en los número menores a 16, es decir, “0d” lo imprime como “d”; lo que causa una diferencia si lo comparas con el hash generado en la línea de comandos de Linux.

Para generar la cadena en la línea de comandos de linux, ejecuta el siguiente comando

echo -n "secret_password" | sha256sum

es importante el parámetro -n en el comando echo para que no agregué un carácter de salto de línea

Fuentes:
http://stackoverflow.com/questions/3021970/which-sha-256-is-correct-the-java-sha-256-digest-or-the-linux-commandline-tool 

GWT cuenta con varias clases para el manejo y manipulación de datos en formato json.

Supongamos que obtenemos la siguiente respuesta del servidor

{“id”:”766″,”nombre”:”cirovladimir”}

Para obtener los valores, lo podemos hacer de la siguiente manera

JSONValue value=JSONParser.parseStrict(json);
String id=value.isObject().get(“id”).isString().stringValue();
String nombre=value.isObject().get(“nombre”).isString().stringValue();

Pero, ¿qué pasa si uno de los valores es nulos? ¿cómo podemos hacer una validación? Supongamos ahora que el servidor nos regresa la siguiente cadena

{“id”:”766″,”nombre”:null}

value.isObject().get(“nombre”)==null //debería ser true, sin embargo, regresa false
value.isObject().get(“nombre”).isNull()==JSONNull.getInstance() //regresa true, esta es la forma correcta

En un reporte de BIRT necesitaba desplegar en el encabezado el mes de acuerdo a un parámetro. Dicho parámetro se recibe como una cadena del “01”-“12” porque para la consulta necesito que sean 2 dígitos. Para determinar el mes, realizaba lo siguiente en el evento “initialize” del reporte:

meses="ENERO","FEBRERO","MARZO","ABRIL","MAYO"
    ,"JUNIO","JULIO","AGOSTO","SEPTIEMBRE","OCTUBRE"
    ,"NOVIEMBRE","DICIEMBRE"];
params["Titulo"]=meses[parseInt(params["MES"])-1]+" del "+params["ANYO"];

Y funcionaba, eso creía yo, hasta que me dijeron que en Agosto(“08”) y Septiembre(“09”) aparecía la leyenda “undefined” en el título del reporte. Pues la causa de esto, es la interpretación que hace la función parseInt cuando le pasamos una cadena que comienza con “0” (recordemos que el código del reporte es Javascript), aquí encuentras la explicación. Para resolverlo, podemos hacer lo siguiente:

params["Titulo"]=meses[parseInt(params["MES"]
    ,10)-1]+" del "+params["ANYO"];

Simplemente le pasamos un segundo parámetro a la función parseInt para indicarle que la cadena(número) que le estamos pasando es en base 10 y no intente interpretarlo como un número octal.
Listo, ya podemos generar el reporte para Agosto y Septiembre 😉

Hace poco integré el CKEditor en un proyecto. A la hora de enviar el código HTML al servidor tuve algunos problemas con los caracteres especiales.
Y es que hay que asegurarnos de que nuestro JSON sea compatible con su definición; afortunadamente el metodo toString de la clase JSONObject se encarga de “escapar” los caracteres no permitidos en JSON (” \ / \b \f \n \r \t \uXXXX) y nos regresa una cadena válida.

String jsonAcuerdo=new JSONObject(acuerdo).toString();

Finalmente, tenemos que enviar los datos dentro de una variable codificada a HTML de la siguiente forma

RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, URL.encode(jsonUrl));
rb.setHeader("Content-type", "application/x-www-form-urlencoded");
StringBuffer postData = new StringBuffer();
postData.append("acuerdo=").append(URL.encodeComponent(jsonAcuerdo));
rb.sendRequest(postData.toString(),new RequestCallback(){
//TODO on response received
});

aquí es importante utilizar el método “encodeComponent” de la clase URL ya que esté si codifica los caracteres delimitadores de componentes en una URL (; / ? : & = + $ , #) a diferencia del método “encode” que no lo hace. Estos caracteres seguramente estarán presentes en el código HTML que deseemos enviar, por ejemplo en &quot; y ni hablar de los otros. Esta variable será decodificada “automaticamente” cuando la leamos en el servidor mediante el método getParameter de la interfaz ServletRequest

En Java no hay forma de hacer un switch sobre una cadena, sin embargo, esta funcionalidad es comun en otros lenguajes.  En java, podemos emular esto de las siguientes maneras con el uso de una enumeración:

public enum Day
{
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY;
}

Dada la enumeracion anterior, podemos hacer el switch de la siguiente forma:

switch (Day.valueOf(str))
{
    case SATURDAY:             
    case SUNDAY:
        // weekend processing …
        break;
    default:
        // weekday processing …
}

El problema con el código anterior es que el método Enum.valueof() lanza una excepción de tipo IllegalArgumentException si la cadena que pasamos como parámetro no corresponde a un valor de la enumeración o NullPointerException si le pasamos un null. Podemos crear un método que atrape estas excepciones y regrese un valor por default.

public enum Day
{
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY,
    NOVALUE;

    public static Day toDay(String str)
    {
        try {
            return valueOf(str);
        }
        catch (IllegalArgumentException ex) {
            return NOVALUE;
        }
    }  
}

Entonces el switch quedaría de la siguiente forma:

switch (Day.toDay(str))
{
    case SUNDAY:               
    case MONDAY:
    case TUESDAY:
        // etc …
    default:
        // any non-Day value
}

Otra manera de implementar la enumeración, pero sin utilizar Excepciones, sería la siguiente:

public enum Day
{
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY,
    NOVALUE;

    public static Day toDay(String str)
    {
        for(Day day:EnumSet.allOf(Day.class)){
       
        if(day.toString().equals(str)){
       
       
        return day;
       
        }
        }
        return Day.NOVALUE;
    }  
}

Fuentes:
Xefer
stackoverflow