Google Gson-deserializar objeto de lista? (tipo genérico)


Quiero transferir un objeto de lista a través de Google Gson, pero no se como deserializar tipos genéricos.

Lo que intenté después de mirar esto (la respuesta de BalusC):

MyClass mc = new Gson().fromJson(result, new List<MyClass>(){}.getClass());

Pero luego recibo un error en eclipse diciendo "El tipo new List(){} debe implementar el método abstracto heredado..."y si uso una solución rápida, obtengo un monstruo de más de 20 talones de método.

Estoy bastante seguro de que hay una solución más fácil, pero parece que no puedo encontrar it!

Editar:

Ahora tengo

Type listType = new TypeToken<List<MyClass>>()
                {
                }.getType();

MyClass mc = new Gson().fromJson(result, listType);

Sin embargo, obtengo la siguiente excepción en la línea "fromJson":

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

I do catch JsonParseExceptions and "result" is not null.

Revisé listType con el depurador y obtuve lo siguiente:

  • tipo de lista
    • args = ListOfTypes
      • list = null
      • Tipos resueltos = Tipo[1]
    • loader = PathClassLoader
    • owntype0 = null
    • ownerTypeRes = null
    • rawType = Class (java.útil.ArrayList)
    • rawTypeName = " java.útil.ArrayList "

Así que parece que la invocación "getClass" no funcionó correctamente. Cualquier sugerencia...?

Edit2: He comprobado en la Guía de usuario de Gson. Menciona una Excepción de tiempo de ejecución que debería ocurrir durante el análisis de un tipo genérico a Json. Lo hice " mal " (no se muestra arriba), al igual que en el ejemplo, pero no obtuve esa excepción en todo. Así que cambié la serialización como se sugiere en la guía del usuario. Pero no ayudó.

Edit3: Resuelto, ver mi respuesta a continuación.

Author: Community, 2011-04-05

13 answers

Método para deserializar la colección genérica:

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

Dado que varias personas en los comentarios lo han mencionado, aquí hay una explicación de cómo se está utilizando la clase TypeToken. La construcción new TypeToken<...>() {}.getType() captura un tipo de tiempo de compilación (entre < y >) en un objeto de tiempo de ejecución java.lang.reflect.Type. A diferencia de un objeto Class, que solo puede representar un tipo raw (borrado), el objeto Type puede representar cualquier tipo en el lenguaje Java, incluida una instanciación parametrizada de un genérico tipo.

La clase TypeToken en sí no tiene un constructor público, porque se supone que no debes construirlo directamente. En su lugar, siempre construye una subclase anónima (de ahí la {}, que es una parte necesaria de esta expresión).

Debido a la eliminación de tipos, la clase TypeToken solo es capaz de capturar tipos que son completamente conocidos en tiempo de compilación. (Es decir, no se puede hacer new TypeToken<List<T>>() {}.getType() para un parámetro de tipo T.)

Para obtener más información, consulte la documentación de para TypeToken clase .

 780
Author: uncaught_exceptions,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-20 12:20:19

Otra forma es usar un array como tipo, por ejemplo:

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

De esta manera evitará toda la molestia con el objeto de tipo, y si realmente necesita una lista, siempre puede convertir la matriz en una lista mediante:

List<MyClass> mcList = Arrays.asList(mcArray);

En mi humilde opinión esto es mucho más legible.

Y para que sea una lista real (que se puede modificar, ver limitaciones de Arrays.asList()), simplemente haga lo siguiente:

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));
 228
Author: DevNG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-07 07:44:08

Consulte este post. Java Type Generic como Argumento para GSON

Tengo una mejor solución para esto. Aquí está la clase wrapper for list para que el wrapper pueda almacenar exactamente el tipo de lista.

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

  @Override
  public Type[] getActualTypeArguments()
  {
      return new Type[] { wrapped };
  }

  @Override
  public Type getRawType()
  {
    return List.class;
  }

  @Override
  public Type getOwnerType()
  {
    return null;
  }
}

Y luego, el código puede ser simple:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}
 25
Author: Happier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:36

Wep, otra forma de lograr el mismo resultado. Lo usamos por su legibilidad.

En lugar de hacer esta oración difícil de leer:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

Cree una clase vacía que extienda una Lista de su objeto:

public class YourClassList extends ArrayList<YourClass> {}

Y úselo al analizar el JSON:

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);
 18
Author: Roc Boronat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-01 10:32:09
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

Ejemplo:

getList(MyClass[].class, "[{...}]");
 7
Author: kayz1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-02 08:13:37

Como responde a mi pregunta original, he aceptado la respuesta de doc_180, pero si alguien se encuentra con este problema de nuevo, voy a responder la segunda mitad de mi pregunta también:

El NullPointerError que describí no tenía nada que ver con la Lista en sí, ¡sino con su contenido!

La clase "MyClass" no tenía un constructor "no args", y tampoco tenía su superclase. Una vez que agregué un constructor simple" MyClass () " a MyClass y su superclase, todo funcionó bien, incluyendo Lista de serialización y deserialización como sugiere doc_180.

 6
Author: jellyfish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-08 08:27:06

Aquí hay una solución que funciona con un tipo definido dinámicamente. El truco es crear el tipo adecuado de matriz usando matriz.newInstance().

    public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}
 4
Author: David Wood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-22 13:51:01

Para Kotlin simplemente:

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<ArrayList<T>>() {}.type

O bien, aquí hay una función útil:

fun <T> buildType(): Type {
    return object : TypeToken<ArrayList<T>>() {}.type
}

Entonces, para usar:

val type = buildType<YourMagicObject>()
 3
Author: Chad Bingham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-23 00:22:24

Quiero añadir una posibilidad más. Si no desea usar TypeToken y desea convertir la matriz de objetos json en una ArrayList, puede proceder de la siguiente manera:

Si su estructura json es como:

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

Y su estructura de clases es como:

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

Entonces puedes analizarlo como:

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

Ahora puede acceder a cada elemento del objeto className.

 1
Author: Apurva Sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-14 09:34:43

Desde Gson 2.8, podemos crear la función util como

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

Ejemplo usando

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);
 1
Author: Phan Van Linh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-21 08:31:15

Me gustó la respuesta de kays1 pero no pude implementarla. Así que construí mi propia versión usando su concepto.

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

Uso:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);
 0
Author: mike83_dev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-20 00:58:07

En mi caso la respuesta de @uncaught_exceptions no funcionó, tuve que usar List.class en lugar de java.lang.reflect.Type:

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
 0
Author: Andrei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-24 15:28:18

Consulte el ejemplo 2 para la comprensión de la clase 'Type' de Gson.

Ejemplo 1: En este deserilizeResturant usamos Employee[] array y obtenemos los detalles

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

Ejemplo 2:

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}
 0
Author: Ram Patro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-12 04:54:55