web-dev-qa-db-fra.com

Désérialisation des types génériques avec GSON

J'ai quelques problèmes avec l'implémentation de Json Deserialization dans mon application Android (avec la bibliothèque Gson)

J'ai fait classe comme ça

public class MyJson<T>{
    public List<T> posts;
}

Et l'appel de désérialisation est:

public class JsonDownloader<T> extends AsyncTask<Void, Void, MyJson<T>> {
...
protected MyJson<T> doInBackground(Void... params) {
  ...
    Reader reader = new InputStreamReader(content);
    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>() {}.getType();
    result = gson.create().fromJson(reader, collectionType);
  ...
  }
}

Le problème est que la liste result.posts après appel contient un tableau d'objets LinkedTreeMap (avec les valeurs correctes, donc le problème est Désérialisation) au lieu d'objets MyJson. Lorsque j'utilise MyObject au lieu de T, tout fonctionne correctement et MyObject est correct.

Alors, est-il possible d'implémenter un appel de désérialisation sans créer de désérialiseur personnalisé?

39
VizGhar

Vous devez spécifier le type de T au moment de la désérialisation. Comment votre List de posts serait-elle créée si Gson ne savait pas quelle Type instancier? Il ne peut pas rester T pour toujours. Ainsi, vous fourniriez le type T en tant que paramètre Class.

En supposant maintenant que le type de posts soit String, vous désérialiseriez MyJson<String> de la manière suivante (j'ai également ajouté un paramètre String json pour plus de simplicité; vous liriez votre reader comme auparavant):

doInBackground(String.class, "{posts: [\"article 1\", \"article 2\"]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // ["article 1", "article 2"]
    return myJson;
}

De même, pour désérialiser une MyJson de Boolean objets

doInBackground(Boolean.class, "{posts: [true, false]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // [true, false]
    return myJson;
}

J'ai supposé que MyJson<T> était le même pour mes exemples

public class MyJson<T> {

    public List<T> posts;

    public List<T> getPosts() {
        return posts;
    }
}

Donc, si vous vouliez désérialiser un List<MyObject>, vous invoqueriez la méthode comme suit:

// assuming no Void parameters were required
MyJson<MyObject> myJson = doInBackground(MyObject.class);
26
Ravi Thapliyal

As-tu essayé?

gson.create().fromJson(reader, MyJson.class);

MODIFIER

Après avoir lu this post, il semble que vous utilisiez Type comme correct. Je crois que votre problème est l'utilisation de T. N'oubliez pas qu'avec Java, il y a effacement de type. Cela signifie qu'au moment de l'exécution, toutes les instances de T sont remplacées par Object. Par conséquent, au moment de l'exécution, ce que vous passez GSON est vraiment MyJson<Object>. Si vous essayez cela avec une classe concrète à la place de <T>, je crois que cela fonctionnerait.

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

24
John B

Donc, la réponse ci-dessus n'a pas fonctionné pour moi, après essais et erreurs, voici comment mon code s'est terminé:

public class AbstractListResponse<T> {
    private List<T> result;

    public List<T> getResult() {
        return this.result;
    }
}

La partie importante ici est la signature de la méthode, y compris le '<T>' à gauche.

protected <T> AbstractListResponse<T> parseAbstractResponse(String json, TypeToken type) {
    return new GsonBuilder()
            .create()
            .fromJson(json, type.getType());
}

Lors de l'appel de Gson, la méthode reçoit le TypeToken de l'objet générique.

TypeToken<AbstractListResponse<MyDTO>> typeToken = new TypeToken<AbstractListResponse<MyDTO>>() {};
AbstractListResponse<MyDTO> responseBase = parseAbstractResponse(json, typeToken);

Et enfin, le TypeToken peut utiliser MyDTO, ou même un simple objet, uniquement MyDTO.

3
Davi Alves

Pour ceux qui luttent avec Kotlin comme moi, j'ai trouvé cette façon de travailler

val type = object : TypeToken<MyJson<MyObject>>() { }.type
val response = gson.fromJson<MyJson<MyObject>>(reader, type)

Notez que l'appel d'une fonction générique nécessite les arguments de type sur le site d'appel après le nom de la fonction (vu ici )

0
tudor