web-dev-qa-db-fra.com

Google Gson - désérialiser la liste <class> objet? (type générique)

Je souhaite transférer un objet de la liste via Google Gson, mais je ne sais pas comment désérialiser les types génériques.

Ce que j'ai essayé après avoir regardé this (réponse de BalusC):

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

mais alors j'obtiens une erreur dans Eclipse disant "Le type new List () {} doit implémenter la méthode abstraite héritée ..." et si j'utilise un correctif rapide, j'obtiens un monstre de plus de 20 stubs de méthode.

Je suis à peu près sûr qu'il existe une solution plus facile, mais il me semble incapable de la trouver!

Modifier:

Maintenant j'ai

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

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

Cependant, je reçois l’exception suivante à la ligne "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)

Je fais attraper JsonParseExceptions et "résultat" n'est pas nul.

J'ai vérifié listType avec le débogueur et j'ai obtenu ce qui suit:

  • type de liste
    • args = ListOfTypes
      • liste = null
      • resolTypes = Type [1]
    • loader = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = Class (Java.util.ArrayList)
    • rawTypeName = "Java.util.ArrayList"

il semble donc que l'invocation "getClass" n'ait pas fonctionné correctement. Aucune suggestion...?

Edit2: J'ai vérifié sur le Guide de l'utilisateur Gson . Il mentionne une exception d'exécution qui devrait se produire lors de l'analyse d'un type générique vers Json. Je l'ai fait "mal" (non montré ci-dessus), comme dans l'exemple, mais je n'ai pas eu cette exception du tout. J'ai donc modifié la sérialisation comme indiqué dans le guide d'utilisation. N'a pas aidé, cependant.

Edit3: résolu, voir ma réponse ci-dessous.

398
jellyfish

Méthode pour désérialiser la collection générique:

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);

Puisque plusieurs personnes dans les commentaires l'ont mentionné, voici une explication de la façon dont la classe TypeToken est utilisée. La construction new TypeToken<...>() {}.getType() capture un type à la compilation (entre le < et le >) dans un objet d'exécution Java.lang.reflect.Type. Contrairement à un objet Class, qui ne peut représenter qu'un type brut (effacé), l'objet Type peut représenter tout type du langage Java, y compris une instanciation paramétrée d'un type générique. .

La classe TypeToken elle-même n'a pas de constructeur public, car vous n'êtes pas censé le construire directement. Au lieu de cela, vous construisez toujours une sous-classe anonyme (d'où le {}, qui est une partie nécessaire de cette expression).

En raison de l'effacement des types, la classe TypeToken ne peut capturer que des types parfaitement connus au moment de la compilation. (Autrement dit, vous ne pouvez pas utiliser new TypeToken<List<T>>() {}.getType() pour un paramètre de type T.)

Pour plus d'informations, reportez-vous à la section documentation de la classe TypeToken .

877

Une autre méthode consiste à utiliser un tableau en tant que type, par exemple:

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

De cette façon, vous évitez tous les problèmes liés à l'objet Type et si vous avez vraiment besoin d'une liste, vous pouvez toujours convertir le tableau en liste en:

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

IMHO c'est beaucoup plus lisible.

Et pour en faire une liste réelle (pouvant être modifiée, voir les limitations de Arrays.asList()), procédez comme suit:

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));
255
DevNG

Reportez-vous à ce post. Type Java générique comme argument pour GSON

J'ai une meilleure solution pour cela. Voici la classe wrapper pour list afin que le wrapper puisse stocker le type exact de liste.

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;
  }
}

Et puis, le code peut être simple:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}
27
Happier

Wep, une autre façon d'atteindre le même résultat. Nous l'utilisons pour sa lisibilité.

Au lieu de faire cette phrase difficile à lire:

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

Créez une classe vide qui étend la liste de votre objet:

public class YourClassList extends ArrayList<YourClass> {}

Et utilisez-le pour analyser le JSON:

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);
23
Roc Boronat

Depuis Gson 2.8, nous pouvons créer une fonction util comme

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

Exemple d'utilisation

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);
22
Phan Van Linh
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);
}

Exemple:

getList(MyClass[].class, "[{...}]");
7
kayz1

Pour Kotlin simplement:

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

ou, voici une fonction utile:

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

Ensuite, utiliser:

val type = buildType<YourMagicObject>()
6
Chad Bingham

Comme cela répond à ma question initiale, j'ai accepté la réponse de doc_180, mais si quelqu'un rencontre à nouveau ce problème, je répondrai également à la seconde partie de ma question:

Le NullPointerError que j'ai décrit n'avait rien à voir avec la liste elle-même, mais avec son contenu!

La classe "MyClass" n'avait pas de constructeur "no args", pas plus que son superclasse. Une fois que j’ai ajouté un constructeur "MyClass ()" simple à MyClass et à sa super-classe, tout a bien fonctionné, y compris la sérialisation et la désérialisation des listes, comme suggéré par doc_180.

6
jellyfish

Voici une solution qui fonctionne avec un type défini dynamiquement. L'astuce consiste à créer le type de tableau approprié à l'aide de Array.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
David Wood

Je veux ajouter une possibilité supplémentaire. Si vous ne voulez pas utiliser TypeToken et que vous voulez convertir un tableau d'objets JSON en un tableau ArrayList, procédez comme suit:

Si votre structure JSON est comme:

{

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

}

et votre structure de classe est comme:

public class ClassName implements Parcelable {

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

alors vous pouvez l'analyser comme ceci:

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

Vous pouvez maintenant accéder à chaque élément de l'objet className.

2
Apurva Sharma

Reportez-vous à l'exemple 2 pour la compréhension de la classe 'Type' de Gson.

Exemple 1: dans ce deserilizeResturant, nous avons utilisé le tableau Employee [] et récupérons les détails.

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());
       }
   }

Exemple 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());
    }
}
1
Ram Patro

Dans mon cas, la réponse de @ uncaught_exceptions ne fonctionnait pas, je devais utiliser List.class au lieu de Java.lang.reflect.Type:

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
0
Andrei

J'ai aimé la réponse de kays1 mais je ne pouvais pas la mettre en œuvre. J'ai donc construit ma propre version en utilisant son concept.

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);
    }
}

Usage:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);
0
mike83_dev