Programación Android, Gson – Librería para parsear documentos JSON

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn15

En nuestras aplicaciones que conecten con servicios web será muy común realizar la comunicación a través de documentos JSON, y hace tiempo explicamos cómo parsear nosotros esos documentos a datos que pudiéramos utilizar en nuestra aplicación. Sin embargo esta tarea es bastante tediosa si tenemos que realizarla para cada una de nuestras conexiones, ya que podemos llegar a necesitar un sinfín de conexiones distintas, y cada una con su propio código para parsear los datos, y para evitar eso tenemos, entre otras, Gson (de código abierto y desarrollada por Google), una librería para parsear documentos JSON. Además, la potencia de este tipo de librerías es que no sólo nos permite pasar de un documento JSON a objetos Java, sino también a la inversa, convertir uno de nuestros objetos en un documento JSON.

En este tutorial vamos a aprender a utilizar Gson, para parsear en ambas direcciones (documento>java / java>documento) y de paso aprovecharemos para aprender a importar librerías que existen en el repositorio Maven a nuestro proyecto en Android Studio.

Qué es Maven?

En primer lugar vamos a entender qué es Maven, y por qué vamos a utilizarlo para obtener nuestra librería Gson. Según Wikipedia Maven es una herramienta de software para la gestión y construcción de proyectos Java creada por Jason van Zyl, de Sonatype, en 2002. Es similar en funcionalidad a Apache Ant (y en menor medida a PEAR de PHP y CPAN de Perl), pero tiene un modelo de configuración de construcción más simple, basado en un formato XML. Estuvo integrado inicialmente dentro del proyecto Jakarta pero ahora ya es un proyecto de nivel superior de la Apache Software Foundation.

Ahora que sabemos qué es Maven, el motivo por el que vamos a utilizar este repositorio en nuestra aplicación es porque Android Studio, a través del uso de Gradle, nos ofrece la posibilidad de descargar estas librerías directamente desde nuestro proyecto, sin necesidad de buscar archivos por internet para después importarlos al proyecto.

Y qué es Gradle?

Gradle es una herramienta para automatizar la construcción de nuestros proyectos de manera muy flexible, por ejemplo las tareas de compilación, testing, empaquetado y el despliegue de los mismos. Pero para que nos entendamos mejor, es una herramienta que nos va a permitir, a través de sencillos comandos, descargar de forma automática las librerías que necesitemos del repositorio Maven.

Integrar Gson en Android Studio


Una vez que sabemos qué es Maven, y qué es Gradle, vamos a crear nuestro proyecto en Android Studio y veremos que en el proyecto hay dos archivos build.gradle:
build.gradle
build.gradle

Si abrimos ambos archivos, veremos que el primero de ellos contiene una serie de instrucciones y una nota que avisa al desarrollador de no poner en este archivo las dependencias que necesite, y por dependencias nos referimos a aquellas librerías de las que depende nuestro proyecto. Eso es porque este primer archivo está ahí para crear la configuración mínima que necesita nuestro proyecto para que podamos empezar a trabajar en él:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

Sin embargo el segundo archivo build.gradle sí está ahí para que nosotros añadamos las dependencias que necesitamos para nuestro proyecto:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "com.proyectosimio.gson"
        minSdkVersion 14
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'
    // Añadir aquí nuestras dependencias.
}

Como podemos ver en el código, he añadido un comentario en el punto en el que debemos añadir la dependencia de Maven que necesitamos. Para poder descargarnos la librería Gson y posteriormente poder usarla en nuestro proyecto, debemos añadir una linea similar al segundo compile, con el que indicaremos en primer lugar el package de la librería que queremos, seguido de dos puntos “:“, el nombre de la librería, de nuevo dos puntos y por último la versión que queremos descargar.

Si hacemos una búsqueda en Google para averiguar estos datos que necesitamos, símplemente poniendo “maven gson“, obtendremos, directamente del repositorio maven, esta información:

<dependency>
	<groupId>com.google.code.gson</groupId>
	<artifactId>gson</artifactId>
	<version>2.2.4</version>
</dependency>

Esta información corresponde con los tres datos que necesitamos para nuestra línea compile, groupId = package al que hacía referencia antes, artifactId = nombre de la librería y la versión. Por lo tanto nuestra línea compile quedaría así:

compile 'com.google.code.gson:gson:2.2.4'

Tras escribir esta nueva línea en pantalla tendremos un mensaje que nos indicará que se ha cambiado algún archivo gradle, y que para que todo funcione bien tendremos que volver a sincronizar. Para hacerlo no tenemos más que darle a “Sync Now” en la parte derecha: sync

Si por algún motivo nos da un error la sincronización, donde antes teníamos “Sync Now” ahora tendremos la opción de ver el log o la vista de mensajes, para averiguar cuál ha sido el error. Una vez que lo hayamos solucionado pulsamos Try Again y volverá a intentar descargar nuestra librería. En cuanto la sincronización haya sido correcta, podremos ver que automáticamente se ha descargado la librería Gson en nuestro proyecto:

Gson - Librería para parsear documentos JSON
Gson

Utilizar Gson – Librería para parsear documentos JSON


Una vez tenemos nuestra librería descargada, sólo nos queda utilizarla y parsear nuestros documentos JSON, o nuestros objetos Java. Como documento JSON de ejemplo he cogido una muestra de la información que nos ofrece la api de flickr:

{
		"title": "Uploads from everyone",
		"link": "http://www.flickr.com/photos/",
		"description": "",
		"modified": "2015-02-09T19:15:27Z",
		"generator": "http://www.flickr.com/",
		"items": [
	   {
			"title": "Wasserschloss Hamburg",
			"link": "http://www.flickr.com/photos/130271032@N08/15862510884/",
			"media": {"m":"http://farm8.staticflickr.com/7379/15862510884_4ffb5c39c1_m.jpg"},
			"date_taken": "2015-02-09T11:15:27-08:00",
			"description": " <p><a href=\"http://www.flickr.com/people/130271032@N08/\">Alex _Lex<\/a> posted a photo:<\/p> <p><a href=\"http://www.flickr.com/photos/130271032@N08/15862510884/\" title=\"Wasserschloss Hamburg\"><img src=\"http://farm8.staticflickr.com/7379/15862510884_4ffb5c39c1_m.jpg\" width=\"240\" height=\"160\" alt=\"Wasserschloss Hamburg\" /><\/a><\/p> ",
			"published": "2015-02-09T19:15:27Z",
			"author": "nobody@flickr.com (Alex _Lex)",
			"author_id": "130271032@N08",
			"tags": "hamburg"
	   },
	   {
			"title": "P2089751-2.jpg",
			"link": "http://www.flickr.com/photos/punimoe/15862511764/",
			"media": {"m":"http://farm8.staticflickr.com/7450/15862511764_2955c62652_m.jpg"},
			"date_taken": "2015-02-08T15:36:31-08:00",
			"description": " <p><a href=\"http://www.flickr.com/people/punimoe/\">punimoe<\/a> posted a photo:<\/p> <p><a href=\"http://www.flickr.com/photos/punimoe/15862511764/\" title=\"P2089751-2.jpg\"><img src=\"http://farm8.staticflickr.com/7450/15862511764_2955c62652_m.jpg\" width=\"180\" height=\"240\" alt=\"P2089751-2.jpg\" /><\/a><\/p> <p>Olympus digital camera<\/p>",
			"published": "2015-02-09T19:15:33Z",
			"author": "nobody@flickr.com (punimoe)",
			"author_id": "23881764@N02",
			"tags": ""
	   }]
}

Para poder ver mejor la estructura del documento JSON os recomiendo utilizar algún tipo de herramienta como esta, que nos permite tanto comprobar la validez del documento como la estructura estándar (recordemos que en el anterior tutorial sobre cómo parsear documentos JSON ya explicamos cómo recoger el documento directamente de la api de flickr).

Ya en la Activity del proyecto, este documento lo almacenaremos en un objeto de tipo String para poder parsearlo posteriormente. Para evitar errores en el proyecto, tendremos que poner la barra de escape (\) delante de todas las dobles comillas del documento JSON. Para facilitaros la tarea os copio exactamente cómo quedaría nuestra variable String lista para usar:

 private final static String jsonDocument = "{" +
            "        \"title\": \"Uploads from everyone\"," +
            "        \"link\": \"http://www.flickr.com/photos/\"," +
            "        \"description\": \"\"," +
            "        \"modified\": \"2015-02-09T19:15:27Z\"," +
            "        \"generator\": \"http://www.flickr.com/\"," +
            "        \"items\": [" +
            "       {" +
            "            \"title\": \"Wasserschloss Hamburg\"," +
            "            \"link\": \"http://www.flickr.com/photos/130271032@N08/15862510884/\"," +
            "            \"media\": {\"m\":\"http://farm8.staticflickr.com/7379/15862510884_4ffb5c39c1_m.jpg\"}," +
            "            \"date_taken\": \"2015-02-09T11:15:27-08:00\"," +
            "            \"description\": \" <p><a href=\\\"http://www.flickr.com/people/130271032@N08/\\\">Alex _Lex<\\/a> posted a photo:<\\/p> <p><a href=\\\"http://www.flickr.com/photos/130271032@N08/15862510884/\\\" title=\\\"Wasserschloss Hamburg\\\"><img src=\\\"http://farm8.staticflickr.com/7379/15862510884_4ffb5c39c1_m.jpg\\\" width=\\\"240\\\" height=\\\"160\\\" alt=\\\"Wasserschloss Hamburg\\\" /><\\/a><\\/p> \"," +
            "            \"published\": \"2015-02-09T19:15:27Z\"," +
            "            \"author\": \"nobody@flickr.com (Alex _Lex)\"," +
            "            \"author_id\": \"130271032@N08\"," +
            "            \"tags\": \"hamburg\"" +
            "       }," +
            "       {" +
            "            \"title\": \"P2089751-2.jpg\"," +
            "            \"link\": \"http://www.flickr.com/photos/punimoe/15862511764/\"," +
            "            \"media\": {\"m\":\"http://farm8.staticflickr.com/7450/15862511764_2955c62652_m.jpg\"}," +
            "            \"date_taken\": \"2015-02-08T15:36:31-08:00\"," +
            "            \"description\": \" <p><a href=\\\"http://www.flickr.com/people/punimoe/\\\">punimoe<\\/a> posted a photo:<\\/p> <p><a href=\\\"http://www.flickr.com/photos/punimoe/15862511764/\\\" title=\\\"P2089751-2.jpg\\\"><img src=\\\"http://farm8.staticflickr.com/7450/15862511764_2955c62652_m.jpg\\\" width=\\\"180\\\" height=\\\"240\\\" alt=\\\"P2089751-2.jpg\\\" /><\\/a><\\/p> <p>Olympus digital camera<\\/p>\"," +
            "            \"published\": \"2015-02-09T19:15:33Z\"," +
            "            \"author\": \"nobody@flickr.com (punimoe)\"," +
            "            \"author_id\": \"23881764@N02\"," +
            "            \"tags\": \"\"" +
            "       }]" +
            "}";

Aprovechando que tenemos la herramienta online para ver la estructura de nuestro documento, vamos a ver exáctamente qué objetos tiene en su interior, y crearemos una clase en Java que contenga estos mismos objetos, además de prestar atención a los tipos. Si un objeto del documento tiene como valor un texto, la variable en Java deberá ser del mismo tipo, si es un entero tendremos que usar un int y para un valor decimal usaremos float. Si lo que queremos es almacenar un tipo Date, en el documento deberá venir como un texto, pero una de las ventajas de usar un parseador como Gson es que podremos configurar el parseador para que los textos que contengan una fecha nos lo parsee a tipo Date.

¿Y qué pasa si el valor del objeto es una colección? Pues en este caso utilizaremos como tipo un ArrayList, y lo parametrizaremos con el tipo de objeto que contenga, ya sea String, int, float… o incluso una clase que creemos nosotros mismos para contener más documentos (como será nuestro caso en este ejemplo y veremos más adelante).

En primer lugar vamos a crear la clase que contiene la información de nuestro documento:

public class Flickr {
    private String title;
    private String link;
    private String description;
    private Date modified;
    private String generator;
    private ArrayList<Item> items;
}

Como vemos en el código hemos creado una variable por cada objeto del documento JSON que tenemos, teniendo varios Strings, un Date y un ArrayList. El objeto Item aún no está definido, posteriormente crearemos una nueva clase con este nombre, y en ella añadiremos la información que tienen los documentos que se encuentran en esa colección. Con esta clase ya hemos recogido toda la estructura básica del JSON, ahora tendríamos que crear todos los getter y setter para todas las variables que hemos creado, quedando la clase así:

public class Flickr {
    private String title;
    private String link;
    private String description;
    private Date modified;
    private String generator;
    private ArrayList<Item> items;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }

    public Date getModified() {
        return modified;
    }

    public void setModified(Date modified) {
        this.modified = modified;
    }

    public String getGenerator() {
        return generator;
    }

    public void setGenerator(String generator) {
        this.generator = generator;
    }

    public ArrayList<Item> getItems() {
        return items;
    }

    public void setItems(ArrayList<Item> items) {
        this.items = items;
    }
}

Ahora sí, pasaremos a crear la clase Item, siguiendo los mismos pasos que hemos seguido para la anterior clase. El resultado sería así:

public class Item {
    private String title;
    private String link;
    private Media media;
    private Date date_taken;
    private String description;
    private Date published;
    private String author;
    private String author_id;
    private String tag;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }

    public Media getMedia() {
        return media;
    }

    public void setMedia(Media media) {
        this.media = media;
    }

    public Date getDate_taken() {
        return date_taken;
    }

    public void setDate_taken(Date date_taken) {
        this.date_taken = date_taken;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Date getPublished() {
        return published;
    }

    public void setPublished(Date published) {
        this.published = published;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getAuthor_id() {
        return author_id;
    }

    public void setAuthor_id(String author_id) {
        this.author_id = author_id;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }
}

Deserializar el documento JSON


Y con esto, por fin, tendríamos ya creadas todas las clases que necesitamos para poder almacenar la información del JSON en un objeto que podamos utilizar nosotros mismos. El siguiente paso sería, de nuevo en nuestra Activity, declarar un objeto Gson y configurar el formato de fechas, y posteriormente declarar otro objeto de tipo Flickr (la primera clase que hemos creado) en la que almacenaremos la información del JSON:

@Override
super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // En el método setDateFormat pasamos el pattern de nuestras fechas. Se añade
    // disableHtmlEscaping() para evitar parseo de símbolos html.
    Gson gson = new GsonBuilder().setDateFormat(DATE_PATTERN).disableHtmlEscaping().create();

    Flickr flickr = gson.fromJson(jsonDocument, Flickr.class);
}

En este caso hemos utilizado un objeto GsonBuilder porque necesitabamos establecer un patrón para la fecha y escapar los símbolos html, en el caso de que no necesitemos ningún tipo de configuración basta con hacer:

Gson gson = new Gson();

En cuanto al patrón que hemos utilizado para la fecha, al analizar el JSON vemos que la fecha viene así: “2015-02-09T19:15:27Z“, por lo tanto el patrón que tendríamos que usar para extraer de ahí la fecha sería:

private final static String DATE_PATTERN = "yyyy-MM-dd'T'hh:mm:ss";

Un dato a tener en cuenta es que en algunos casos en el documento JSON de ejemplo aparece la fecha junto con el Time Zone, (añade al final algo así como -08:00), esto en Java 1.7 y superiores se puede parsear añadiendo al final del patrón que acabamos de definir una “X”, sin embargo esto en Android nos dará excepción por no reconocer ese caracter, por lo que simplemente perderemos el Time Zone al parsear el documento.

Para ver el significado del patrón, podemos ver la documentación de la clase SimpleDateFormat de Java.

En este punto ya tendríamos toda la información del JSON en nuestro objeto flickr, y podríamos utilizarlo según lo necesitáramos. Por ejemplo el siguiente código:

Flickr flickr = gson.fromJson(jsonDocument, Flickr.class);
Log.d(TAG, new SimpleDateFormat("dd/MM/yyyy hh:mm:ss").format(flickr.getModified()));

Produce el siguiente output en el logcat:
2050-2050/com.proyectosimio.gson D/MainActivity﹕ 09/02/2015 07:15:27

Al mismo tiempo, si analizáramos el objeto flickr debugueando veríamos que nuestro ArrayList se ha rellenado con los 2 objetos que contenía el array del documento JSON, sin que hayamos necesitado configurar nada adicional.

El siguiente paso en nuestro tutorial sería convertir este objeto que tenemos en Java en una documento JSON, para eso Gson nos permite deserializar nuestra clase para extraer el documento JSON a partir de sus datos, aunque esta es la parte más sencilla de todas. Se haría simplemente llamando al método toJson del objeto gson, y le pasaríamos como parámetro el objeto que queremos parsear a JSON:

String json = gson.toJson(flickr);

Y con esto ya tendríamos parseada toda la información que contuviera el objeto que parseamos. Tan sólo tendríamos que tener en cuenta que si alguno de los campos de nuestro objeto son null (por ejemplo no hemos definido una de las fechas), en el documento JSON no aparecería. Si deseamos que aparezca este campo en el JSON como null, en la inicialización del objeto gson tendríamos que añadir el método serializeNulls:

Gson gson = new GsonBuilder().setDateFormat(DATE_PATTERN).disableHtmlEscaping().serializeNulls().create();

Configuración extra

En el ejemplo hemos visto que hemos dado el mismo nombre a las variables que las claves que contiene el documento JSON, para que de esta forma la librería automáticamente detecte los campos y los rellene. Sin embargo es posible que las claves en el JSON no respondan a una nomenclatura estándar en Java (por ejemplo estar en mayúscula la clave completa), o que directamente el nombre de la clave no sea lo suficientemente claro y por lo tanto nuestro campo no lo sería tampoco (por ejemplo nuestra última clase tiene una variable “m” que no resulta nada aclaratoria de lo que contendrá).

Para evitar este tipo de situaciones, es posible dar el nombre que deseemos a nuestras variables, y a través de una anotación indiquemos a la librería cuál será la clave del documento JSON que se identifique con esa variable. Esta anotación consistiría en escribir justo antes de la declaración de la variable que deseemos renombrar @SerializedName(“nombreClave”). Por ejemplo para el caso de nuestra clase Media quedaría así:

public class Media {
    @SerializedName("m")
    private String media;

    public String getMedia() {
        return media;
    }

    public void setMedia(String media) {
        this.media = media;
    }
}

Descargas

Puedes acceder a nuestro repositorio para bajar el proyecto aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn15
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

13 thoughts on “Programación Android, Gson – Librería para parsear documentos JSON

  1. Hola que tal IAvilaE, soy nuevo en programacion android y me encargaron una aplicacion que en un listView adjuntara un json string, no tendras un ejemplo por favor te lo agradeceria mucho

  2. Un tutorial bastante profesional y una explicaciòn minusiosa de como integrar gson, justamente lo que estaba buscando, creo que me cuesta un poco de trabajo gradle.

  3. Hola como estan son nueva en esto de android, mi problema es el siguiente tengo un web service que devuelve una lista de varios usuarios, lo que quiero es pasar a un ArrayList cada uno de los usuarios y quiero utilizar la libreria GSON

    1. Buenas Irene,

      La forma de hacerlo es exactamente como he explicado en el tutorial, tienes que crear clases que contengan los mismos datos que el documento json. El tutorial es más extenso que sólo leer un array, no tienes más que poner en tu modelo un array de “items” y esos items tendrían que tener los campos de tus objetos en tu documento.

  4. Creo que no me he explicado bien, esto que comento se puede utilizar con gson. Viene muy bien cuando tienes, por ejemplo, la respuesta de un API que te devuelve mucha morralla. Imagina que la informacion que necesitas esta varios niveles por abajo de la respuesta (ejemplo response.items.data.(aqui tus datos) ) no te vas a crear clases para todo (Response, Items, Data). Para hacer eso tienes que create una clase que implemente JsonDeserializer y en la función “deserialize” jugar con el json para formar tu objeto con exclusivamente los datos que necesitas.

    Imagina que has hecho una clase llamada MyUnserializer que implementa JsonDeserializer. Para usarla con gson solo tienes que hacer:

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(MyObject.class, new MyUnserializer());
    Gson gson = gsonBuilder.create();

  5. Para el siguiente como evitar tener que crear “una clase en Java que contenga estos mismos objetos”, es decir obtener solo, por ejemplo, un respuesta con un objeto simple: title e items. Con JsonDeserializer lo hago yo, hay alguna forma alternativa?

    1. La verdad es que no te sabría responder. Siempre he tenido que trabajar con objetos que han tenido la misma estructura que el documento JSON y no he tenido la necesidad de hacerlo como tú comentas.

      Posiblemente a través del GsonBuilder puedas añadir alguna configuración para hacerlo así. Otra librería que también está muy extendida es Jackson, también es posible que pueda trabajar como comentas.

      Un saludo.

        1. Efectivamente, te había entendido mal.

          Esto que comentas sí que lo he visto en alguna ocasión, sin embargo tampoco he tenido que hacerlo porque siempre he trabajado con APIs privadas que me devolvían exactamente lo que necesitaba tener. Pero todo lo que he visto al respecto trabajaba exactamente como tú has comentado.

Deja un comentario