Parsear Datos JSON En Android Con JsonReader Y Gson

¿Quieres saber cómo leer datos JSON en Android?

¿Te gustaría aprender formas rápidas y comprensibles para convertir objetos JSON en objetos Java?

¿Además de todo quieres ubicar tus datos en un ListView?

Si te quedas y sigues leyendo hasta el final, tus preguntas serán respondidas a través de varios ejemplos prácticos.

¿Qué es JSON?

JSON (Javascript Object Notation) es un formato de intercambio de datos entre clientes y servidores,  basado en la sintaxis de Javascript para representar estructuras en forma organizada y alto nivel, con el fin de acercar a una definición mucho más amigable por los desarrolladores.

Su notación de datos se asimila a la creación de objetos en Javascript, pero esto no significa que su funcionamiento dependa de este. JSON es un formato en texto plano independiente de todo lenguaje de programación, es más, soporta el intercambio de datos en gran variedad de lenguajes de programación como PHP, Python, C++, C#, Java y Ruby.

JSON es una herramienta potente en el desarrollo de aplicaciones web, ya que facilita el desarrollo y comprensión de intercambio  de datos. Si recuerdas, habíamos visto que XML también puede usarse para el intercambio, pero debido a que su definición genera un DOM, el parseo se vuelve extenso y pesado.

Lee También Tutorial básico del lenguaje XML

Además de ello XML debe usar XPath para especificar rutas de elementos y atributos, por lo que demora la reconstrucción de la petición. En cambio JSON no requiere restricciones adicionales, simplemente se obtiene el texto plano y el engine de Javascript en los navegadores hace el trabajo de parsing sin ninguna complicación.

Tipos de datos en JSON

Similar a la estructuración de datos primitivos y complejos en los lenguajes de programación, JSON establece varios tipos de datos: cadenas, números, booleanos, arrays, objetos y valores null.

El propósito es crear objetos que contengan varios atributos compuestos como pares clave-valor. Donde la clave es un nombre que identifique el uso del valor que lo acompaña.

Veamos un ejemplo:

{
"Id": 101
"Nombre": "Carlos",
"EstaActivo": true,
"Notas": [ 2.3, 4.3, 5.0]
}

La anterior estructura es un objeto JSON compuesto por los datos de un estudiante. Los objetos JSON contienen sus atributos entre llaves “{}”, al igual que un bloque de código en Javascript, donde cada atributo debe ir separado por coma ‘,’ para diferenciar cada par.

La sintaxis de los pares debe contener dos puntos ‘:’ para dividir la clave del valor. El nombre del par debe tratarse como cadena y añadirle comillas dobles.

Si notas, este ejemplo trae un ejemplo de cada tipo de dato:

  • El Id es de tipo entero, ya que contiene un número que representa el código del estudiante.
  • El Nombre es un string. Usa comillas dobles para identificarlas.
  • EstaActivo es un tipo booleano que representa si el estudiante se encuentra en la institución educativa o no. Usa las palabras reservadas true y false para declarar el valor.
  • Notas es un arreglo de números reales. El conjunto de sus elementos debes incluirlos dentro de  corchetes “[ ]” y separarlos por coma.

Usar la clase JsonReader para parsear un arreglo JSON

En la sección anterior se discutió una pequeña introducción a la sintaxis de JSON como preámbulo para expresar los propósitos de este capítulo.

Debido a la integración que ha tenido la información en la nube, las aplicaciones ya no tienen límites. Si estas creando un servicio web en el cual tu usuario puede tener acceso a sus datos y características desde cualquier dispositivo, entonces el intercambio de datos con tu servidor será de gran importancia en la versión de tu aplicación Android.

La idea es crear un mecanismo que permita recibir la información que contiene la base de datos externa en formato JSON hacia la aplicación. Con ello se parseará cada elemento y será interpretado en forma de objeto Java para integrar correctamente el aspecto en la interfaz de usuario.

La clase JsonReader de Java es ideal para interpretar datos con formato JSON. Provee un sistema poderoso para el parseo de arreglos y objetos  embebidos en las respuestas de los servidores. No obstante, no se puede considerar como un parser en sí mismo, si no como una clase auxiliar para crear tus propios parsers.

Veamos una ilustración que muestra el proceso de parseo que será estudiado:
Convertir objetos JSON en objetos Java en Android

Como puedes observar el origen de los datos es un servidor externo o hosting que hayas contratado como proveedor para tus servicios web. Ignoraremos la aplicación web que realiza la gestión de encriptación de los datos a formato JSON (puede ser PHP, JavaScript, ASP.NET, etc.).

Tu aplicación Android a través de un cliente realiza una petición GET a la dirección URL del recurso con el fin obtener los datos. Ese flujo entrante debe interpretarse con ayuda de un parser personalizado que implementa la clase JsonReader.

El resultado final es un conjunto de datos adaptable al API de Android. Dependiendo de tus necesidades, puedes convertirlos en una lista de objetos estructurados que alimenten un adaptador que pueble un ListView o simplemente actualizar la base de datos local de tu aplicación en SQLite.

Para codificar el proceso de parsin mostrado en el diseño de la ilustración, se usará un prototipo de aplicación llamada ZooWak. Esta aplicación muestra datos característicos de una lista de animales salvajes que se encuentra almacenada en la base de datos de un servidor externo.
Aplicación Android ZooWak

Los registros de los animales son enviados serializados en formato JSON desde el servidor. A continuación se muestra la lista de ejemplo que se usará (Se truncó el atributo descripción por comodidad):

[  
   {  
      "especie":"Leu00f3n",
      "descripcion":"El leu00f3n (Panthera leo) es un mamu00edfero carnu00edvoro ...",
      "imagen":"leon"
   },
   {  
      "especie":"Elefante",
      "descripcion":"Los elefantes o elefu00e1ntidos (Elephantidae) son ...",
      "imagen":"elefante"
   },
   {  
      "especie":"Cocodrilo",
      "descripcion":"Los crocodu00edlidos (Crocodylidae) son una familia de sauru00f3psidos ...",
      "imagen":"cocodrilo"
   },
   {  
      "especie":"Cebra",
      "descripcion":"Se conocen como cebra (o zebra, grafu00eda en desuso ) a tres ...",
      "imagen":"cebra"
   },
   {  
      "especie":"u00c1guila",
      "descripcion":"El u00e1guila calva (Haliaeetus leucocephalus), tambiu00e9n ...",
      "imagen":"aguila"
   }
]

Zoowak solo tiene una actividad en la que se encuentra un ListView, por lo que se deduce que la salida del proceso de parsing debe ser de tipo estructurado, en este caso es una pieza de código personalizada llamada Animal. Dicho elemento debe converger en el diseño establecido para cada ítem de la lista, así que el adaptador debe inflar elementos en base a dicha configuración.

Crear un parser personalizado con la clase JsonReader

Vamos a crear una clase llamada JsonAnimalParser la cual interpretará un flujo de datos con formato JSON y retornará en una lista de  objeto de tipo Animal.

La definición de la clase Animal es solo un reflejo en Java de los atributos de los objetos JSON que vienen desde el servidor. Veamos:

public class Animal {
    private String especie;
    private String descripcion;
    private String imagen;

    public Animal(String especie, String descripcion, String imagen) {
        this.especie = especie;
        this.descripcion = descripcion;
        this.imagen = imagen;
    }

    public String getEspecie() {
        return especie;
    }

    public void setEspecie(String especie) {
        this.especie = especie;
    }

    public String getDescripcion() {
        return descripcion;
    }

    public void setDescripcion(String descripcion) {
        this.descripcion = descripcion;
    }

    public String getImagen() {
        return imagen;
    }

    public void setImagen(String imagen) {
        this.imagen = imagen;
    }
}

El formato Json contiene los campos especie, descripción e imagen de la base de datos que está en el servidor externo. Sabiendo sus claves, es posible interpretar cada objeto del arreglo. Cabe destacar que el código Json viene formateado con el estándar UTF-8, por eso se ven tantos códigos en donde van las tildes. Esto nos permite conservar el acento de nuestros datos de texto sin ninguna dificultad.

Ahora observa los pasos para crear nuestro parser:

Paso 1: Leer el formato general Json

Lo primero que se debe implementar es un método que coordine el retorno de los objetos de tipo animal y que además cree una instancia del JsonReader. Veamos:

 public List<Animal> readJsonStream(InputStream in) throws IOException {
    // Nueva instancia JsonReader
    JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
    try {
        // Leer Array
        return leerArrayAnimales(reader);
    } finally {
        reader.close();
    }

}

El tipo de retorno de la función es una lista de animales, ya que es justo el tipo de estructura que recibirá nuestro adaptador. Además de ello, se crea el lector Json asociando el flujo de entrada y el tipo de encriptado que usarás, en este caso UTF-8. Con ello los acentos serán interpretados automáticamente.

Si todo finalizó con éxito, entonces el lector se cierra con close() para limpiar espacios de memoria referenciada.

Paso 2: Leer el array de objetos Json

Se creó la función leerArrayAnimales() para recorrer todo el array enviado desde el servidor. Recibe como parámetro el lector Json para continuar la labor de lectura. Luego se apunta al primer elemento del array con el método beginArray(), lo que nos permite leer con un bucle while el siguiente elemento con el método hasNext().

public List leerArrayAnimales(JsonReader reader) throws IOException {
    // Lista temporal 
    ArrayList animales = new ArrayList();

    reader.beginArray();
    while (reader.hasNext()) {
        // Leer objeto
        animales.add(leerAnimal(reader));
    }
    reader.endArray();
    return animales;
}

hasNext() pregunta al array si existen más objetos aún, por lo que si la respuesta es positiva se añaden los elementos a una lista temporal llamada animales a través del método leerAnimal().

Cuando se acaben los objetos, entonces se debe liberar la memoria, cerrando el array con el método endArray(). Finalmente se retorna la lista temporal con todos los animales.

Paso 3: Leer los atributos de cada objeto Json

El método leerAnimal() accede a todos pares clave-valor que convirtió el lector JsonReader en datos interpretables. Así que aprovechando esa estructura definida, es posible recorrer cada objeto particular en busca de los valores que se refieran a una clave.

public Animal leerAnimal(JsonReader reader) throws IOException {
    String especie = null;
    String descripcion = null;
    String imagen = null;

    reader.beginObject();
    while (reader.hasNext()) {
        String name = reader.nextName();
        switch (name) {
            case "especie":
                especie = reader.nextString();
                break;
            case "descripcion":
                descripcion = reader.nextString();
                break;
            case "imagen":
                imagen = reader.nextString();
                break;
            default:
                reader.skipValue();
                break;
        }
    }
    reader.endObject();
    return new Animal(especie, descripcion, imagen);
}

Similar a la situación del array, debemos iniciar el objeto con beginObject() para apuntar al primer par. Luego comenzamos un bucle para la lectura de todos los pares con el método análogo hasNext(). Donde se obtiene la clave del par con el método nextName() y se asigna a una variable local.

Ya con ese valor es posible abrir una estructura switch que relacione todos los valores de las claves posibles que tiene tu objeto. En cada comparación debes usar los métodos next() para obtener el valor dependiendo del tipo y asignarlo a una variable temporal. Si tu valor es String, entonces usarás nextString(), si es de tipo int, entonces usas nextInt(), y así sucesivamente.

En el caso default puedes usar el método skipValue(), el cual permite saltar valores que no te interesan. Si de pronto hubiésemos consultado el ID del animal, entonces se usaría este método para evitar su lectura, ya que no interesa en el proceso actual.

Por último retorna en un nuevo objeto Animal que alimente su constructor de todas las variables temporales obtenidas.

Poblar un ListView desde datos Json

El proceso anterior ya es el 90% de la aplicación ZooWak, ahora solo queda crear el adaptador y relacionar los objetos.
El adaptador que vamos a crear tiene ítems con un diseño particular, ya que se incorporará una imagen representativa por cada animal al layout. Las imágenes que usaremos están dentro de la carpeta drawable del proyecto. Cada una tiene asignado el nombre exacto que existe en la base de datos del servidor externo.

Lee También Listas y Adaptadores en Android

Crear diseño de los ítems del ListView

Antes de definir el layout para los ítems que poblarán el ListView echémosle un vistazo al siguiente boceto:
Boceto de Item de un ListView en Android

Se organizarán los atributos del objeto Json con un formato enriquecido que resuma las características del animal. Primero usaremos la especie del animal como título general de cada ítem, en este caso puedes apreciar que es “León”. Directamente en su límite bottom-top va la imagen que se obtiene de los recursos. Y en último lugar se ubica la descripción del animal justo en el límite bottom-top de la imagen.

¿Sencillo cierto?

Ahora fíjate la implementación del layout en XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <TextView
        android:id="@+id/especieAnimal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerInParent="true"
        android:background="?android:selectableItemBackground"
        android:clickable="true"
        android:focusable="true"
        android:textSize="24sp" />

    <ImageView
        android:id="@+id/imagenAnimal"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_below="@+id/especieAnimal"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/descAnimal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/imagenAnimal"
        android:layout_centerInParent="true"
        android:background="?android:selectableItemBackground"
        android:clickable="true"
        android:focusable="true" />

</RelativeLayout>

La definición es solo un RelativeLayout más textviews y un imageview con centerCrop para ajustar el tamaño del archivo .jpg, algo que tu ya conoces muy bien.

Lee también Configurar Layouts y Views en Android Studio

Crear Adaptador personalizado para la lista

Extenderemos una clase llamada AdaptadorDeAnimales de la clase ArrayAdapter para sobrescribir el método getView() con el fin de empotrar el archivo item_lista.xml y obtener el drawable correspondiente a través del nombre de la imagen.

public class AdaptadorDeAnimales extends ArrayAdapter {

    public AdaptadorDeAnimales(Context context, List objects) {
        super(context, 0, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){

        //Obteniendo una instancia del inflater
        LayoutInflater inflater = (LayoutInflater)getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        //Salvando la referencia del View de la fila
        View v = convertView;

        //Comprobando si el View no existe
        if (null == convertView) {
            //Si no existe, entonces inflarlo
            v = inflater.inflate(
                    R.layout.item_lista,
                    parent,
                    false);
        }

        //Obteniendo instancias de los elementos
        TextView especieAnimal = (TextView)v.findViewById(R.id.especieAnimal);
        TextView descAnimal = (TextView)v.findViewById(R.id.descAnimal);
        ImageView imagenAnimal = (ImageView)v.findViewById(R.id.imagenAnimal);


        //Obteniendo instancia de la Tarea en la posición actual
        Animal item = getItem(position);

        especieAnimal.setText(item.getEspecie());
        descAnimal.setText(item.getDescripcion());
        imagenAnimal.setImageResource(convertirRutaEnId(item.getImagen()));

        //Devolver al ListView la fila creada
        return v;

    }

    private int convertirRutaEnId(String nombre){
        Context context = getContext();
        return context.getResources()
                .getIdentifier(nombre, "drawable", context.getPackageName());
    }
}

Lo único fuera de lo común en este código es la obtención del identificador de la imagen a través de su nombre con el método getIdentifier().

Este método recibe como parámetro el nombre del recurso, que en este caso lo obtuvimos desde el objeto Json. También recibe el nombre del tipo de recurso y el paquete donde se encuentra. Siendo este último accedido desde el método getPackageName() de la clase Context.

Estas instrucciones las ubicamos dentro del método convertirRutaEnId(), el cual recibe el nombre de la imagen y retorna el entero representativo del identificador del recurso.

Obtener datos Json con una petición HTTP

Ya teniendo toda la infraestructura necesaria para dirigir el flujo de datos con formato Json, es hora de realizar la petición GET correspondiente. Para ello se recurre al el cliente HttpURLConnection como se vio en el post anterior.
Lee también Operaciones HTTP en Android con el cliente HttpURLConnection

La petición la realizaremos en el método doInBackground() de una tarea asíncrona  personalizada llamada JsonTask. Luego crearemos el adaptador y lo relacionaremos con la lista en onPostExecute().
Lee también Uso de Hilos y tareas asíncronas(AsyncTask) en Android

Veamos:

public class JsonTask extends AsyncTask<URL, Void, List<Animal>>{

        @Override
        protected List doInBackground(URL... urls) {
            List animales = null;

            try {

                // Establecer la conexión
                con = (HttpURLConnection)urls[0].openConnection();
                con.setConnectTimeout(15000);
                con.setReadTimeout(10000);

                // Obtener el estado del recurso
                int statusCode = con.getResponseCode();

                if(statusCode!=200) {
                    animales = new ArrayList();
                    animales.add(new Animal("Error",null,null));

                }
                else{

                
                // Parsear el flujo con formato JSON                  
                    InputStream in = new BufferedInputStream(con.getInputStream());

                    // JsonAnimalParser parser = new JsonAnimalParser();
                    GsonAnimalParser parser = new GsonAnimalParser();

                    animales = parser.readJsonStream(in);

                }

            } catch (Exception e) {
                e.printStackTrace();

            }finally {
                con.disconnect();
            }
            return animales;
        }

        @Override
        protected void onPostExecute(List<Animal> animales) {
            /*
            Asignar los objetos de Json parseados al adaptador
             */
            if(animales!=null) {
                adaptador = new AdaptadorDeAnimales(getBaseContext(), animales);
                lista.setAdapter(adaptador);
            }else{
                Toast.makeText(
                        getBaseContext(),
                        "Ocurrió un error de Parsing Json",
                        Toast.LENGTH_SHORT)
                .show();
            }

        }
    }

Finalmente el resultado visual de la aplicación es el siguiente:
Aplicación Android que muestra lista de Animales

Descargar Código De La Aplicación Android

Si deseas obtener el código final y evitar seguir todos los pasos del tutorial, entonces puedes desbloquear su link de descargar suscribiéndote a Hermosa Programación:

La Librería GSON En Android

La librería Gson es otra forma de parsear un formato Json a objetos Java. Gson es de código abierto y se encuentra alojada en http://code.google.com/p/google-gson.
Para usar el repositorio en nuestro proyecto de Android Studio debemos añadir la siguiente dependencia a el archivo build.gradle:

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

Esta herramienta provee al desarrollador un conjunto de métodos para convertir objetos Json a objetos Java  y viceversa con un minimo esfuerzo. Además permite relacionar objetos serializados Java a una estructura genérica en Json.

Convertir datos Java a Json

La clase principal de la librería es Gson. Para implementar sus funcionalidades debemos instanciar un nuevo objeto y llamar el método deseado.

Para convertir datos atómicos en formato JSON debes usar el método toJson(). Veamos algunos ejemplos:

Gson gson = new Gson();

int entero= 2;
Log.d("Formato Json entero", gson.toJson(entero));

String cadena ="Gson";
Log.d("Formato Json cadena", gson.toJson(cadena));

int arreglo []= {1, 2, 3, 4};
Log.d("Formato Json arreglo", gson.toJson(arreglo));

toJson() retorna en un string que representa la estructura Json dependiendo del tipo que se usó como parámetro. Las impresiones anteriores serían:

Formato Json entero: 2
Formato Json cadena: "Gson"
Formato Json arreglo: [1, 2, 3, 4]

También es posible convertir un objeto completo a formato Json:

Log.d("Objeto Animal con formato Json", gson.toJson(
            new Animal (
                    "Lobo",
                    "Los lobos son animales carnívoros que andan en manadas…",
                    "lobo")));

Donde la impresión sería (se muestra con espacios por comodidad):

{  
   "especie":"Lobo",
   "descripcion":"Los lobos son animales carnívoros que andan en manadas…",
   "imagen":"lobo"
}

Convertir formato Json a objetos Java

Es posible realizar el proceso inverso con el método fromJson(), el cual recibe como parámetro el valor y la clase de dato:

int entero = gson.fromJson("1", int.class);

String cadena = gson.fromJson(""abc"", String.class);

Boolean booleano = gson.fromJson("true",Boolean.class);

char[] arreglo = gson.fromJson("[m,n,o,p,q]",char[].class);

En nuestro caso específico necesitamos reconstruir los objetos que nos arroja la clase JsonReader a través de la lectura del flujo externo, para crear la lista de animales. Esto solo funcionará si la cantidad y tipo de los atributos del objeto Json coinciden con la definición de la clase Java.

Para ello crearemos una nueva clase llamada GsonAnimalParser, en la cual crearemos el mismo método leerFlujoJson() de JsonAnimalParser e incluiremos una combinación de lectura entre JsonReader y Gson.

public class GsonAnimalParser {


    public List leerFlujoJson(InputStream in) throws IOException {
        Gson gson = new Gson();
        JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
        List animales = new ArrayList();

        reader.beginArray();

        while (reader.hasNext()) {
            Animal animal = gson.fromJson(reader, Animal.class);
            animales.add(animal);
        }


        reader.endArray();
        reader.close();
        return animales;
    }
}

Con la ayuda de la librería Gson nos ahorramos la lectura selectiva de los pares clave-valor del objeto Json, ya que reemplazamos la lectura del objeto por el método fromJson().

Conclusiones

JSON es un formato de intercambio de datos supremamente flexible y amigable a los ojos de cualquier programador. Estas características hacen que sea indispensable para la comunicación entre dispositivos que complementan sus datos en un servicio en la nube.

JsonReader es una herramienta efectiva para traducir flujos de datos sin pulir en formato Json. Aunque trae consigo métodos para la conversión a objetos Java, requiere crear manualmente la implementación de la lectura.

Precisamente el trabajo de Gson es la conversión rápida de objetos Java a Json y viceversa, lo que hace que se complemente exitosamente con la clase JsonReader. Aunque vimos varios ejemplos del uso de Gson, no significa que ese sea el máximo de su potencial.

Gson trae consigo características más complejas que pueden serte de utilidad en futuros proyectos con distintas necesidades. Visita su página principal para averiguar hasta el fondo.

Notas: Imágenes autoría de FreePik.

  • Fco Javier Navas Ceballos

    Buenos días James,

    Ante todo darte las gracias, porque ya estaba desesperado buscando algo así.

    Necesito poner un OnClick para cuando pulse sobre un animal (por ejemplo) se abra otra Activity o realice otra función que nosotros queramos.

    Espero que me puedas ayudar, porque llevo ya días liado con esto.

    Gracias y un saludo.

  • Ro Menacho

    Genial, muchas gracias!

  • Matias

    Buenas tardes James,
    una consulta, resulta que el formato de mi json es el siguiente:

    {“pool”:[{“nroPool”:”408″,”descripcion”:”Kit S.Smash – Ruta 5″,”pathImagenBannerMiniatura”:””},{“nroPool”:”407″,”descripcion”:”140/60-17 MRF”,”pathImagenBannerMiniatura”:””},{“nroPool”:”406″,”descripcion”:”Kit G.Smash – Ruta 5″,”pathImagenBannerMiniatura”:””}]}

    es decir que se encuentra todo dentro de “pool”.

    Quisiera me des una guía de como manipular el adapter para adaptarlo a mi tipo de json.
    Un cordial saludo.

  • Jose H.

    Ya me salió el ejemplo, pero lo que no me queda claro de que hosting esta agarrando la información? ósea el .json donde esta alojado ? en que hosting? :s

    [
    {
    “especie”:”Leu00f3n”,
    “descripcion”:”El leu00f3n (Panthera leo) es un mamu00edfero carnu00edvoro …”,
    “imagen”:”leon”
    },

    ETC…

    • Jose H.

      Ya lo ví, esta en el MainActivity JAJA. Gracias y saludos.

  • jose

    muy bueno el tutorial, he descargado tu código pero a la hora de ejecutar mi tira el siguiente error y no he podido solucionarlo…

    Error:(30, 32) error: no suitable method found for fromJson(android.util.JsonReader,Class)

    method Gson.fromJson(JsonElement,Type) is not applicable

    (no instance(s) of type variable(s) T#1 exist so that argument type android.util.JsonReader conforms to formal parameter type JsonElement)

    method Gson.fromJson(JsonElement,Class) is not applicable

    (no instance(s) of type variable(s) T#2 exist so that argument type android.util.JsonReader conforms to formal parameter type JsonElement)

    method Gson.fromJson(com.google.gson.stream.JsonReader,Type) is not applicable

    (no instance(s) of type variable(s) T#3 exist so that argument type android.util.JsonReader conforms to formal parameter type com.google.gson.stream.JsonReader)

    method Gson.fromJson(Reader,Type) is not applicable

    (no instance(s) of type variable(s) T#4 exist so that argument type android.util.JsonReader conforms to formal parameter type Reader)

    method Gson.fromJson(Reader,Class) is not applicable

    (no instance(s) of type variable(s) T#5 exist so that argument type android.util.JsonReader conforms to formal parameter type Reader)

    method Gson.fromJson(String,Type) is not applicable

    (no instance(s) of type variable(s) T#6 exist so that argument type android.util.JsonReader conforms to formal parameter type String)

    method Gson.fromJson(String,Class) is not applicable

    (no instance(s) of type variable(s) T#7 exist so that argument type android.util.JsonReader conforms to formal parameter type String)

    where T#1,T#2,T#3,T#4,T#5,T#6,T#7 are type-variables:

    T#1 extends Object declared in method fromJson(JsonElement,Type)

    T#2 extends Object declared in method fromJson(JsonElement,Class)

    T#3 extends Object declared in method fromJson(com.google.gson.stream.JsonReader,Type)

    T#4 extends Object declared in method fromJson(Reader,Type)

    T#5 extends Object declared in method fromJson(Reader,Class)

    T#6 extends Object declared in method fromJson(String,Type)

    T#7 extends Object declared in method fromJson(String,Class)

    si alguien puede ayudarme, se lo agradeceré mucho.

  • jose

    muy bueno el tutorial, he descargado tu código pero a la hora de ejecutar mi tira el siguiente error y no he podido solucionarlo…

    Error:(30, 32) error: no suitable method found for fromJson(android.util.JsonReader,Class)

    method Gson.fromJson(JsonElement,Type) is not applicable

    (no instance(s) of type variable(s) T#1 exist so that argument type android.util.JsonReader conforms to formal parameter type JsonElement)

    method Gson.fromJson(JsonElement,Class) is not applicable

    (no instance(s) of type variable(s) T#2 exist so that argument type android.util.JsonReader conforms to formal parameter type JsonElement)

    method Gson.fromJson(com.google.gson.stream.JsonReader,Type) is not applicable

    (no instance(s) of type variable(s) T#3 exist so that argument type android.util.JsonReader conforms to formal parameter type com.google.gson.stream.JsonReader)

    method Gson.fromJson(Reader,Type) is not applicable

    (no instance(s) of type variable(s) T#4 exist so that argument type android.util.JsonReader conforms to formal parameter type Reader)

    method Gson.fromJson(Reader,Class) is not applicable

    (no instance(s) of type variable(s) T#5 exist so that argument type android.util.JsonReader conforms to formal parameter type Reader)

    method Gson.fromJson(String,Type) is not applicable

    (no instance(s) of type variable(s) T#6 exist so that argument type android.util.JsonReader conforms to formal parameter type String)

    method Gson.fromJson(String,Class) is not applicable

    (no instance(s) of type variable(s) T#7 exist so that argument type android.util.JsonReader conforms to formal parameter type String)

    where T#1,T#2,T#3,T#4,T#5,T#6,T#7 are type-variables:

    T#1 extends Object declared in method fromJson(JsonElement,Type)

    T#2 extends Object declared in method fromJson(JsonElement,Class)

    T#3 extends Object declared in method fromJson(com.google.gson.stream.JsonReader,Type)

    T#4 extends Object declared in method fromJson(Reader,Type)

    T#5 extends Object declared in method fromJson(Reader,Class)

    T#6 extends Object declared in method fromJson(String,Type)

    T#7 extends Object declared in method fromJson(String,Class)

    si alguien puede ayudarme, se lo agradeceré mucho.

  • Kira Yamato Zara

    Hola buenas James, tengo un problema y no se el motivo, tengo mi propio webservice aunque hecho en Java en mi maquina en local, he copiado el codigo de tu aplicación y me funciona con la URL de tu webservice.

    Pero con mi propiowebservice me da error que creo es de conexion, lo tengo en local, he probado el webservice y funciona bien (incluso desde otra maquina).

    Aqui dejo el logcat

    10-30 10:22:33.486 11095-11095/? W/dalvikvm: VFY: unable to resolve interface method 17910: Landroid/view/Window$Callback;.onWindowStartingActionMode (Landroid/view/ActionMode$Callback;I)Landroid/view/ActionMode;

    10-30 10:22:33.486 11095-11095/? D/dalvikvm: VFY: replacing opcode 0x72 at 0x0002

    10-30 10:22:33.507 11095-11109/? W/System.err: java.net.ConnectException: failed to connect to localhost/127.0.0.1 (port 8080) after 15000ms: isConnected failed: ECONNREFUSED (Connection refused)

    10-30 10:22:33.507 11095-11109/? W/System.err: at libcore.io.IoBridge.isConnected(IoBridge.java:214)

    10-30 10:22:33.507 11095-11109/? W/System.err: at libcore.io.IoBridge.connectErrno(IoBridge.java:152)

    Es mas extenso pero creo que el fallo es que me rechaza la conexión y no se si a alguno mas le habra pasado.

    Solo estoy cambiando el campo:

    if (networkInfo != null && networkInfo.isConnected()) {
    new JsonTask().
    execute(
    new URL(“http://localhost:8080/AplicacionJsonAndroid/webresources/com.aplicacionjsonandroid.animal/”));
    } else {
    Toast.makeText(this, “Error de conexión”, Toast.LENGTH_LONG).show();
    }

    Asi lo tengo y me salta el toast de “Ocurrio un error de parsing”

    Se me ha escapado modificar alguna linea mas del código de la aplicación? He estado buscando pero no encuentro.

    El json que le estoy pasando es proveniente de una base de datos propia pero creada de forma similar a la tuya, devuelve el mismo json:

    [{“descripcion”:”Cosa que vuela y va por el aire”,”especie”:”Aguila”,”imagen”:”Aguila Roja”}]
    No es solo uno, pero por no ocupar mas espacio.

    • Amadeus Portes

      Puedes intentar cambiando la direccion localhost por la de tu ip dentro de la red.
      Por ejemplo cambiar el localhost (127.0.0.1) por 192.168.1.72

    • Hola compañero. Creo que ECONNREFUSED salta porque no tienes el wifi o red habilitada en tu emulador o dispositivo. Revisa y me cuentas

  • German

    Hola James , practicando hice el mismo ejemplo con un recyclerview y cargando los datos en un cardview pero me tira un error y no lo encuentro , te paso el proyecto a ver si tenes tiempo de verlo y quizas te sirve para publicarlo , https://www.dropbox.com/sh/c9os4hn25x2lqyx/AACspuAoyDvomxlMiXbAdc5oa?dl=0

    saludos y Gracias

    • Hola German.

      Me queda duro revisar todo el proyecto amigo.

      Si pegas el error del logcat yo y los otros compañeros podríamos ver que sucede. Así tendrías mejor ayuda.

      Saludos!

      • Germán

        El tema es que no encuentro el error en el logcat ja, bueno si alguien exporta el proyecto y me puede ayudar. Gracias, saludos.

        • Quiere decir que no sale ni un registro o el logcat queda en blanco?

          • German

            no identifico ningun error en el logcat , si lo podes descargar y exportarlo asi como esta te lo agradezco, por ahi es una pavada el error y no me estoy dando cuenta , gracias saludos

          • German

            James te dije que era una pavada jaja , a veces es mejor despejar la mente un rato , el error se encontraba en el adaptador me estaba olvidando de sobrescribir el getitemCount() ,, :) Gracias por tu tiempo y los tutos.

          • jajajaja a bueno German.

            Relax entonces

  • Joaquín Naftaly

    Me tira error en Animal item = getItem(position); Sabes por que puede ser?

    • Hola Joaquín.

      Si pegas un pantallazo de lo que dice el logcat podremos ver mejor.

    • Ro Menacho

      Proba con esto:

      Animal item = (Animal) this.getItem(position);

  • Jorge Luis

    Gracias por el tutorial, me ha ayudado mucho, podrias subir un ejemplo de como leer un json que contenga un tipo de dato de tipo arreglo. Gracias.

  • ISC. Jose Ma Martinez

    hola acabo de empezar a programar aplicaciones android, tu tutorial me ah servido apara tener las primeras nociones, sin duda alguna esta muy bien explicado y es muy útil :D ,pero bueno, una duda si quisiera mandar los elementos de especie, descripción e imagen a una nueva activity como podria hacerlo, lo eh intentado con putExtra pero solo recibo los id de los Textview

    • Hola Jose. ¿A que te refieres con que solo recibes los ids de los textview?

      • ISC. Jose Ma Martinez

        puse un onClick en el textView de especie y llamo a un metodo como este
        public void mandaOnClick(View v){

        TextView especie= (TextView) findViewById(R.id.atEspecie)

        Intent i = new Intent(this,Main2Activity.class);

        i.putExtra(“especie”, especie);

        startActivity(i);
        }

        de este lo recibo con getExtra y lo pongo en un toast

        en este solo me aparece una serie de numeros

        • ISC. Jose Ma Martinez

          ya vi mi error era algo asi lo que tenia que hacer:

          public void mandaOnClick(View v){

          TextView especie= (TextView) v.findViewById(R.id.atEspecie);
          String Especie1= titulo.getText().toString();
          Intent i = new Intent(this,Main2Activity.class);

          i.putExtra(“Especie1”, Especie1);

          startActivity(i);
          }

          gracias nuevamente por el buen tutorial :D

          • Ok compeñero :D

          • Matias

            Buenas tardes José,
            este es un problema que hace mucho tiempo estoy queriendo resolver y no me resulta posible.
            Consulto: en … “titulo.getText().toString();”, que vendría a ser “titulo” ?
            Agradeceré tu respuesta.

            Saludos

  • Cristopher Juan Greta Quispe

    Una consulta como se puede enviar imagenes por JSON y recibirlos por ANDROID

    • Es posible. Para ello debes interpretar tu imagen en forma de bytes y reconstruirla en tu app.

      Mira este tuto a ver que tal: http://mobile.cs.fsu.edu/converting-images-to-json-objects/

      Por otro lado, simplemente puedes enviar la URL de la imagen en el objeto Json y descargarla en la app como se hace en este tutorial.

      • Cristopher Juan Greta Quispe

        Excelente, gracias !!

        • Con gusto amigo

          • Cristopher Juan Greta Quispe

            Asi una consulta amigo, tambien vas a llegar subir tutoriales de IOS tan buenos como los de android ?

          • Me gustaría, pero la verdad es que no se mucho sobre el tema.

  • Asier

    Hola! En primer lugar dar las gracias por este gran tutorial, me esta sirviendo de mucho y esta muy bien explicado.
    Tengo un problema, estoy intentando parsear una lista de comentarios que tienen como atributos el contenido, el creador y la fecha de cada uno, cuando hago el reader.beginArray() me vuelve al metodo readJsonStream() en el close(). El inputStream que le entra he mirado que convertido a String tiene una longitud de 50. ¿Porqué puede ser? ¿Hay alguna manera de enviarte el codigo, si no te importara?

    Un saludo!

    • Hola Asier.

      La verdad no te comprendo bien. ¿Se produce algún error? ¿Puedes mostrar un pantallazo del log cat por aquí?

      • Asier

        Gracias por contestar James, para explicarte mejor te dejo aqui un enlace a los tres ficheros implicados en el problema. Uno es una clase que se encarga de llamar al método de la clase que obtiene los comentarios, la otra es la tarea asíncrona y finalmente desde esta tarea se llama al parser. Cuando llego al parser, y este accede a los métodos, al llegar al reader.beginArray(), me salta el error con el log que te aparece en la carpeta que te comparto. Es esta: https://www.dropbox.com/sh/qxpf5yevljusbq8/AAC1rYWcyO5tvYgJHB4nvYCoa?dl=0

        Un saludo y mil gracias!!

        • Hola Asier.

          Que tal si pones un Log con todo el contenido del formato Json que está llegando a la aplicación.

          Pega un pantallazo del log aquí en el gestor de comentarios.

          • Asier

            Como puedo ver el contenido del inputstream y ponerlo aqui? O se supone que puedo ver el contenido en formato json cuando se obtiene desde el inputstream?

          • A lo que me refiero es que despues del parsing de tu stream quedará un string que muestra el Json de forma entendible. Justo esa cadena es la que te digo que imprimas en el Log para ver su contenido.

            O mejor, cuando tengas el Json lo pegas aquí http://jsonformatter.curiousconcept.com/ para mejorar su lectura y luego si me mandas un pantallazo por este medio.

          • Asier

            Hola James, no se me estaba leyendo porque cuando hago el beginArray no encuentra un array sino lo que ves a continuación en la captura. Es una locura, no se cómo voy a moverme por un fichero tan enrevesado para obtener el contenido, id, etc de cada usuario… http://www.jsoneditoronline.org/?id=b73dc60acd235f60a647c02ed3e068db Desde ese directorio puedes ver el codigo Json.

            Gracias!

          • Hola Asier.

            NO entiendo porque en tu parser estás leyendo los atributos “content”, “isRecomment” y “creator”.

            El formato Json que me mandaste comienza con “links”, “total”, y “_embedded”.

            ¿Seguro ese es el formato?

            Mira, para que te quede más fácil, crea un método por cada objeto, array y atributo que exista en los elementos. Solo debes seguir de mayor a menor la jerarquía. Presta atención a la jerarquía antes de construir el parsing.

            No se si esté equivocado, pero no concuerdan tus atributos.

  • Jorge

    Claro James, permiteme solicitar permiso a la empresa, de que forma lo puedo compartir? te dejo algun link de mega upload o algo asi?

    Saludos

    • James Revelo

      Si, algún servicio donde no tenga que registrarme para descargar :D

    • James Revelo

      Si es muy confidencial, solo crea una pequeña versión donde quede aislado la petición HTTP y que al ejecutar se muestre el problema de la respuesta a medias en Android Studio.

      • Jorge

        Correcto James, dejame modificar o crear una prueba donde me de el error y te lo mando enseguida, tienes algun mail donde te pueda mandar la liga? gracias y saludos.

        • James Revelo

          Ve a la sección de contacto de la página y me lo envías por allí

  • Chase

    Hola muy buen Tutorial, quisiera descargarlo, ya le di retuit, pero no me aparece el link de descarga, saludos.

    • Chase

      Olvidalo, ya vi el link.

  • Jorge

    Antes que nada felicidades por tu tutorial, tengo una pregunta y espero me puedas ayudar a resolverla, al momento de trar u obtener la informacionde la web cuando mi celular esta conectado con wifi me devuelve la cadena completa de la informacion pero cuando me conecto por 3G solo me deuelve 1000 carcateres en la mayoria de los casos?

    gracias y saludos.

    • James Revelo

      Hola Jorge.

      ¿Estas usando una tarea asíncrona para realizar la petición?

      • Jorge

        Si asi es, efectivamente, estoy usando una tarea sincronizada para realizar la peticion, mi codigo es el siguiente:

        @Override
        protected String doInBackground(String… urls) {
        RsResult=””;

        //Antes de ejecutar query, primero reviso que tenga conexion a internet, sino no hago nada
        if (!RevisoConexionInternet.verificaConexion(Contexto)) {
        Toast.makeText(Contexto, “No se tiene conexión a internet, por favor compruebe sus datos.”, Toast.LENGTH_LONG).show();
        return null;
        }
        else {
        try {
        return loadFromNetwork(url);
        } catch (IOException e) {
        try {
        JSONObject json = new JSONObject(RsResult);
        return RsResult;
        } catch (JSONException e1) {
        e1.printStackTrace();
        return null;
        }
        }
        }
        }

        /** Initiates the fetch operation. */
        private String loadFromNetwork(String urlString) throws IOException {
        InputStream stream = null;
        String str =””;

        try {
        stream = downloadUrl(urlString);
        str = readIt(stream, 50000);
        //str = “Error conectando a la base de datos.”; //Emulando error
        } finally {
        if (stream != null && str != “”) {
        stream.close();
        }
        }
        str = Html.fromHtml(str).toString();
        RsResult= str;
        return str;
        }

        private InputStream downloadUrl(String urlString) throws IOException {
        InputStream myInputStream =null;
        Uri.Builder builder = new Uri.Builder()
        .appendQueryParameter(“myqueryx”, QClave)
        .appendQueryParameter(“Qvalor”, QValor)
        .appendQueryParameter(“Qvalor2”, QValor2)
        .appendQueryParameter(“Qvalor3”, QValor3)
        .appendQueryParameter(“Qvalor4”, QValor4);
        String query = builder.build().getEncodedQuery();
        URL url;
        try {
        url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setRequestMethod(“POST”);

        conn.setReadTimeout(40000 );
        conn.setConnectTimeout(60000 );

        OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
        // this is were we’re adding post data to the request
        wr.write(query);
        wr.flush();
        myInputStream = conn.getInputStream();
        wr.close();
        } catch (Exception e) {
        //handle the exception !
        //Log.d(TAG,e.getMessage());
        }
        return myInputStream;
        }

        /** Reads an InputStream and converts it to a String.
        * @param stream InputStream containing HTML from targeted site.
        * @param len Length of string that this method returns.
        * @return String concatenated according to len parameter.
        * @throws IOException
        * @throws UnsupportedEncodingException
        */
        private String readIt(InputStream stream, int len) throws IOException {
        Reader reader;
        reader = new InputStreamReader(stream, “utf-8”);
        char[] buffer = new char[len];
        reader.read(buffer);
        return new String(buffer);
        }

        MUCHAS GRACIAS POR RESPONDER. Llevo mucho tiempo buscando este error, que puede ser?

        • Jorge

          Hola te dejo el codigo de mi java completo para que no te falta nada por si necesitas analizarlo:

          import android.content.Context;
          import android.net.Uri;
          import android.os.AsyncTask;
          import android.text.Html;
          import android.widget.Toast;
          import org.json.JSONArray;
          import org.json.JSONException;
          import org.json.JSONObject;
          import java.io.IOException;
          import java.io.InputStream;
          import java.io.InputStreamReader;
          import java.io.OutputStreamWriter;
          import java.io.Reader;
          import java.io.UnsupportedEncodingException;
          import java.net.HttpURLConnection;
          import java.net.URL;

          public class MySql extends AsyncTask
          {
          String[][] InfaRegresar = new String[0][];
          String QClave;
          String QValor;
          String QValor2;
          String QValor3;
          String QValor4;
          Context Contexto;
          String[] Campos;
          boolean Error;

          String url;
          String RsResult = “”;

          MySql(String pQClave, String pValor1, String pValor2, String pValor3, String pValor4, Context Contexto, String[] Campos) {
          // list all the parameters like in normal class define
          this.QClave = pQClave;
          this.QValor = pValor1;
          this.QValor2 = pValor2;
          this.QValor3 = pValor3;
          this.QValor4 = pValor4;
          this.Contexto = Contexto;
          this.Campos = Campos;
          this.Error = false;
          }

          @Override
          protected void onPreExecute() {
          super.onPreExecute();
          url = “http://misitio.com.mx/misquerys.php”;
          }

          @Override
          protected void onPostExecute(String strings) {
          super.onPostExecute(strings);

          if (!strings.equals(“Error conectando a la base de datos.”) && !strings.equals(“”)) {
          try {
          JSONObject json = new JSONObject(RsResult);
          if (!json.getString(“rsresult”).equals(“Sin resultados”)) {
          JSONArray ArregloRs = new JSONArray(json.getString(“rsresult”));
          //Coloco las dimensiones del arreglo a devolver
          InfaRegresar = new String[Campos.length][ArregloRs.length()];

          for (int j = 0; j < Campos.length; j++) {
          InfaRegresar[j][0] = "*ERROR CAMPO*";
          }

          for (int j = 0; j < Campos.length; j++) {
          for (int i = 0; i < ArregloRs.length(); i++) {
          JSONObject RsItem = ArregloRs.getJSONObject(i);
          InfaRegresar[j][i] = RsItem.getString(Campos[j]);
          }
          }
          } else {
          Error = true;
          }
          } catch (JSONException e) {
          e.printStackTrace();
          }
          }
          else {
          Error = true;
          InfaRegresar = new String[1][1];
          InfaRegresar[0][0]= "Error conectando a la base de datos.";
          }

          switch (QClave){
          case "QAVISOG":
          MenuPrincipal.ColocaMensage(InfaRegresar[0][0]);
          break;

          case "QBUSCACPORNOMBRE":
          seleccionarcliente.Consulta_por(InfaRegresar);
          break;
          }
          }

          @Override
          protected String doInBackground(String… urls) {
          RsResult="";

          //Antes de ejecutar query, primero reviso que tenga conexion a internet, sino no hago nada
          if (!RevisoConexionInternet.verificaConexion(Contexto)) {
          Toast.makeText(Contexto, "No se tiene conexión a internet, por favor compruebe sus datos.", Toast.LENGTH_LONG).show();
          return null;
          }
          else {
          try {
          return loadFromNetwork(url);
          } catch (IOException e) {
          try {
          JSONObject json = new JSONObject(RsResult);
          return RsResult;
          } catch (JSONException e1) {
          e1.printStackTrace();
          return null;
          }
          }
          }
          }

          /** Initiates the fetch operation. */
          private String loadFromNetwork(String urlString) throws IOException {
          InputStream stream = null;
          String str ="";

          try {
          stream = downloadUrl(urlString);
          str = readIt(stream, 50000);
          //str = "Error conectando a la base de datos."; //Emulando error
          } finally {
          if (stream != null && str != "") {
          stream.close();
          }
          }
          str = Html.fromHtml(str).toString();
          RsResult= str;
          return str;
          }

          private InputStream downloadUrl(String urlString) throws IOException {
          InputStream myInputStream =null;
          Uri.Builder builder = new Uri.Builder()
          .appendQueryParameter("myqueryx", QClave)
          .appendQueryParameter("Qvalor", QValor)
          .appendQueryParameter("Qvalor2", QValor2)
          .appendQueryParameter("Qvalor3", QValor3)
          .appendQueryParameter("Qvalor4", QValor4);
          String query = builder.build().getEncodedQuery();
          URL url;
          try {
          url = new URL(urlString);
          HttpURLConnection conn = (HttpURLConnection) url.openConnection();
          conn.setDoOutput(true);
          conn.setRequestMethod("POST");

          conn.setReadTimeout(40000 );
          conn.setConnectTimeout(60000 );

          OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
          // this is were we're adding post data to the request
          wr.write(query);
          wr.flush();
          myInputStream = conn.getInputStream();
          wr.close();
          } catch (Exception e) {
          //handle the exception !
          //Log.d(TAG,e.getMessage());
          }
          return myInputStream;
          }

          /** Reads an InputStream and converts it to a String.
          * @param stream InputStream containing HTML from targeted site.
          * @param len Length of string that this method returns.
          * @return String concatenated according to len parameter.
          * @throws IOException
          * @throws UnsupportedEncodingException
          */
          private String readIt(InputStream stream, int len) throws IOException {
          Reader reader;
          reader = new InputStreamReader(stream, "utf-8");
          char[] buffer = new char[len];
          reader.read(buffer);
          return new String(buffer);
          }
          }

          //Esto lo llamo cada vez que requiero obtener informacion de mi sitio web, espero sea sufuciente para poder ayudarme a solucionar el problema que me trae mareado desde ya hace tiempo, gracias!!!

          • James Revelo

            Hola Jorge. Comparteme el proyecto completo en .rar lo reviso.
            Saludos!

        • Bro, Gracias por este tutorial. Una pregunta ¿Que pasaria si la web que quiero visualizar en el app usando json o el metodo get si me pide password y contraseña para motrar la web en el app de nuestro android? por ejeplo quiero desarrollar un app para la universidad, la pagina de la universidad tiene un user login page donde cada estudiante ingresa su password y username para poder visualizar su información de usuario como por ejemplo: los resultados de sus calificaciones entre otras cosas. ¿puedo lograr esto en una app creando un user login para que luego json pueda visualizar la información cuando el usuario haya iniciado sesion?

          Realmente no se mucho de esto pero estoy aprendiendo ;)

          • James Revelo

            Si claro. Debes crear un servicio web que implemente JSON y gestione las cuentas de los usuarios. Obviamente es todo un trabajo de bases de datos y desarrollo web para que puedas consumir los datos desde tu aplicación.

  • Adrian

    Que buen tutorial man!

    • James Revelo

      Gracias mi hermano!