Páginas

miércoles, 13 de febrero de 2013

Primeros pasos con JSON y Java

Introducción

JSON (JavaScript Object Notation), es un formato estándar de intercambio de datos basado en texto que permite a las aplicaciones intercambiar información.

JSON está estandarizado por la IETF (Internet Engineering Task Force) en el RFC-4627 (http://tools.ietf.org/html/rfc4627). El Mime Type para datos en éste formato es 'application/json' y la extensión oficial de los ficheros con este formato es '.json'.

Los datos intercambiados son independientes tanto de la plataforma como del lenguaje de programación utilizado.

¿Qué ventajas ofrece JSON frente a XML?

JSON va reemplazando a XML poco a poco como medio preferido a la hora de intercambiar datos entre aplicativos de distintas plataformas. Los motivos por los que se argumenta que JSON es mejor son casi siempre los mismos:
  • JSON es más fácil de leer (xml también).
  • JSON tiene una estructura es similar a la de Objetos y Arrays (xml también).
  • JSON es más ligero (bytes) en las transmisiones (desde luego que no hay etiqueta de cierre, pero sí de apertura).
  • JSON se parsea más rápido (supongo que será verdad, pero entiendo que dependerá de la calidad del parseador).

Sintaxis JSON

En este apartado vamos a repasar las principales características de la sintaxis JSON, se puede encontrar fácilmente documentación detallada sobre el tema, por ejemplo en el enlace siguiente: http://www.json.org

Estructuras de datos en JSON

JSON ofrece únicamente tres estructuras de datos:
   
- Pares Nombre/Valor. Un ejemplo sería:
   
            {
                "nombre" : "nombre11"
            }

           
- Objetos. Son colecciones desordenadas de pares Nombre/Valor. Un ejemplo sería:
       
            {
                "contacto" : {
                    "nombre" : "nombre11",
                    "apellido1" : "apellido11",
                    "apellido2" : "apellido21",
                    "email" : "11111111@gmail.com"
                }
            }

           
- Arrays. Son colecciones ordenadas de objetos. Un ejemplo sería:
           
            {
                "agenda" : [
                    {"nombre" : "nombre11", "apellido1" : "apellido11", "apellido2" : "apellido21", "email" : "11111111@gmail.com"},
                    {"nombre" : "nombre12", "apellido1" : "apellido12", "apellido2" : "apellido22", "email" : "22222222@gmail.com"},
                    {"nombre" : "nombre13", "apellido1" : "apellido13", "apellido2" : "apellido23", "email" : "33333333@gmail.com"}
                ]
            }

Tipos de datos en JSON

Hasta el momento hemos visto que en la parte derecha de cada par "Nombre/Valor" aparecía siempre una cadena entrecomillada. JSON admite además de cadenas (String) algunos otros tipos de datos:
  • Object
  • Array
  • String
  • Number
  • Boolean
  • null

Comentarios en JSON

JSON no admite comentarios.

Convenciones en el nombrado de atributos

Los nombres de los atributos en JSON (el elemento izquierdo de cada par Nombre/Valor) se escriben en notación "camel case", que es típica también en Java. La idea es escribir los nombres compuestos metiendo mayúscula al comienzo de cada palabra excepto en la primera. Por ejemplo: "telefFijo", "telefMovil", etc.

Esquemas en JSON

Los documentos JSON, como cabría esperar, están basados en esquemas. Los esquemas definen la estructura de los documentos JSON y permiten validarlos. Los esquemas JSON están evolucionando muy rápidamente (la versión 0.4 está publicada en: http://tools.ietf.org/html/draft-zyp-json-schema-04
   
El sitio de internet donde más esquemas JSON hay publicados es: http://json-schema.org, pero visto el número de esquemas ¿estándares? publicados parece que el tema está aún muy verde. Parece que aún está lejos de sustituir al XML en este sentido.

Herramientas para trabajar con JSON

A la hora de empezar a trabajar con JSON pueden ser interesantes las siguientes herramientas:
  • Validador online JSON. Revisar la sintaxis de un documento JSON grande a ojo es verdaderamente pesado, por tanto puede ser interesante utilizar un validador online como el ofrecido en http://www.jsonlint.com para validar nuestros documentos.
  • Editor JSON. Si lo que necesitamos es un editor JSON donde generar documentos o esquemas con cierto nivel de asistencia, una herramienta muy popular es JSONPad, descargable en: http://www.jsonpad.com/en/Home.html. También hay editores online, siendo uno de los más populares: http://jsoneditoronline.org/

JSON y Java

A la hora de programar aplicaciones que generen o traten datos en formato JSON hay muchos API:
  • Jackson. http://jackson.codehaus.org
  • Google GSON. http://code.google.com/p/google-json/
  • SOJO. http://sojo.sourceforge.net/

Aplicativo Java de pruebas

El siguiente proyecto Java muestra un ejemplo de cómo realizar las siguientes operaciones:
  • Crear un documento JSON a partir de un POJO
  • Crear un POJO a partir de un documento JSON
  • Obtener el esquema de un documento JSON
  • Validar un documento JSON contra un esquema   
Se han utilizado las siguientes librerías:

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-lgpl</artifactId>
            <version>1.9.12</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <type>jar</type>
        </dependency>

El código son dos clases, la clase Contacto.java y la clase TestJSON.java:
   
package es.ine.sgtic.pojo;

public class Contacto {

    private String nombre;
    private String apellido1;
    private String apellido2;
    private String telefFijo;
    private String telefMovil;
    private String email;

    public Contacto(){
        //Constructor que precisa JSON
    }
    public Contacto(final String nombre,
            final String apellido1,
            final String apellido2,
            final String telefFijo,
            final String telefMovil,
            final String email) {
        this.nombre = nombre;
        this.apellido1 = apellido1;
        this.apellido2 = apellido2;
        this.telefFijo = telefFijo;
        this.telefMovil = telefMovil;
        this.email = email;
    }
    public String getNombre() {
        return nombre;
    }
    public void setNombre(final String nombre) {
        this.nombre = nombre;
    }
    public String getApellido1() {
        return apellido1;
    }
    public void setApellido1(final String apellido1) {
        this.apellido1 = apellido1;
    }
    public String getApellido2() {
        return apellido2;
    }
    public void setApellido2(final String apellido2) {
        this.apellido2 = apellido2;
    }
    public String getTelefFijo() {
        return telefFijo;
    }
    public void setTelefFijo(final String telefFijo) {
        this.telefFijo = telefFijo;
    }
    public String getTelefMovil() {
        return telefMovil;
    }
    public void setTelefMovil(final String telefMovil) {
        this.telefMovil = telefMovil;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(final String email) {
        this.email = email;
    }

    public boolean equals(Contacto cto) {
        return this.nombre.equals(cto.getNombre()) &&
                this.apellido1.equals(cto.getApellido1()) &&
                this.apellido2.equals(cto.getApellido2()) &&
                this.telefFijo.equals(cto.getTelefFijo()) &&
                this.telefMovil.equals(cto.getTelefMovil()) &&
                this.email.equals(cto.getEmail());
    }
}


package es.ine.sgtic.pojo;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.SerializationConfig.Feature;
import org.codehaus.jackson.schema.JsonSchema;

public class TestJSON {

    private static Log log = LogFactory.getLog(TestJSON.class);
   
    public static void main (String args[]) 

                       throws JsonGenerationException, 
                       JsonMappingException, IOException {

        //Instanciamos el convertidor Java-JSON (funcionaría en ambos sentidos
        final ObjectMapper mapper = new ObjectMapper();

        //Ajustamos el convertidor para que indente las cadenas JSON genedaras
        mapper.configure(Feature.INDENT_OUTPUT, true);

        //Montamos un pojo de la clase Contacto
        final Contacto cto1 = new Contacto("nombre","apellido1","apellido2",

                                  "123456789","987654321","egdp1970@gmail.com");

        log.trace("Generamos el String cadenaJSON asociado al objeto cto1");
        String cadenaJSON = generaNotacionJSON(mapper, cto1);
        log.trace(cadenaJSON);
        log.trace("----------------------------------------------------");
       
        log.trace("Generamos un objeto cto2 a partir del String cadenaJSON");
        final Contacto cto2 = (Contacto)generaPojojava(mapper, cadenaJSON, cto1.getClass());
        log.trace("Comparamos el objeto cto1 y el objeto cto2");
        log.trace("cto1.equals(cto2)-->"+cto1.equals(cto2));
        log.trace("----------------------------------------------------");

        log.trace("Generamos String cadenaJSON asociado al objeto cto2");
        cadenaJSON = generaNotacionJSON(mapper, cto2);
        log.trace(cadenaJSON);
        log.trace("----------------------------------------------------");

        log.trace("Generamos el String schemaJSON asociado al objeto cto1");
        final String schemaJSON = generaSchemaJSON(mapper, cto1.getClass());
        log.trace(schemaJSON);
        log.trace("----------------------------------------------------");

        log.trace("Validamos el String cadenaJSON contra el esquema schemaJSON");
        log.trace("validaCadenaJSON()-->"+validaCadenaJSON(mapper, cadenaJSON));
        log.trace("----------------------------------------------------");
       
        log.trace("Validamos el String cadenaJSON_ERR contra el esquema schemaJSON");
        final String cadenaJSON_ERR = cadenaJSON + "}";
        log.trace("validaCadenaJSON()-->"+validaCadenaJSON(mapper, cadenaJSON_ERR));
        log.trace("----------------------------------------------------");
    }

    private static String generaNotacionJSON(final ObjectMapper mapper, 

                                             Object obj) 
                                              throws JsonGenerationException, 
                                              JsonMappingException, 
                                              IOException{

        //Convertimos el objeto Java en notación JSON
        final Writer writer = new StringWriter();
        mapper.writeValue(writer, obj);

        //Devolvemos la cadena JSON
        return writer.toString();
    }

    private static Object generaPojojava(final ObjectMapper mapper, 

                                         final String cadenaJSON, 
                                         final Class<?> clazz) 
                                              throws JsonGenerationException, 
                                              JsonMappingException, 
                                              IOException{

        //Convertimos la notación JSON en objeto Java
        return mapper.readValue(cadenaJSON, clazz);
    }

    private static String generaSchemaJSON(final ObjectMapper mapper, 

                                           final Class<?> clazz)                                                                      throws JsonGenerationException, 
                                              JsonMappingException, 
                                              IOException{

        //Generamos el schema JSON para el pojo
        final SerializationConfig cfg = mapper.getSerializationConfig();
        final JsonSchema jsonSchema = mapper.generateJsonSchema(clazz,cfg);
        return jsonSchema.toString();
    }

    private static boolean validaCadenaJSON(final ObjectMapper mapper, 

                                            final String cadenaJSON){

        boolean valido = false;
        try {
            final JsonParser parser = mapper.getJsonFactory().createJsonParser(cadenaJSON);
            while (parser.nextToken() != null) {
            }
            valido = true;
        } catch (JsonParseException e) {
           
//log.error(e.getMessage(),e);
        } catch (IOException e) {
           
//log.error(e.getMessage(),e);
        }

        return valido;
    }
}

No hay comentarios:

Publicar un comentario