web-dev-qa-db-fra.com

Gson TypeToken avec le type d'élément ArrayList dynamique

J'ai ce code:

Type typeOfObjectsList = new TypeToken<ArrayList<myClass>>() {}.getType();
List<myClass> objectsList = new Gson().fromJson(json, typeOfObjectsList);

Il convertit une chaîne JSON en un List d'objets. Mais maintenant, je veux avoir ce ArrayList avec un type dynamique (pas seulement myClass), défini au moment de l'exécution.

Le type d'élément de ArrayList sera défini avec une réflexion .

J'ai essayé ceci:

    private <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
        Type typeOfObjectsListNew = new TypeToken<ArrayList<T>>() {}.getType();
        return typeOfObjectsListNew;
    }

Mais ça ne marche pas. C'est l'exception:

Java.sql.SQLException: Fail to convert to internal representation: {....my json....}
64
Amin Sh

La syntaxe que vous proposez n'est pas valide. Le suivant

new TypeToken<ArrayList<Class.forName(MyClass)>>

n'est pas valide car vous essayez de passer un appel de méthode pour lequel un nom de type est attendu.

Le suivant

new TypeToken<ArrayList<T>>() 

n'est pas possible à cause du fonctionnement des génériques (effacement des types) et de la réflexion. L'ensemble TypeToken hack fonctionne parce que Class#getGenericSuperclass() effectue les opérations suivantes

Retourne le Type représentant la superclasse directe de l'entité (classe, interface, type primitif ou void) représentée par cette classe.

Si la superclasse est un type paramétré, l'objet Type renvoyé doit refléter avec précision les paramètres de type réels utilisés dans le code source.

En d’autres termes, s’il voit ArrayList<T>, C’est le ParameterizedType qu’il retournera et vous ne pourrez pas extraire la valeur de compilation de la variable type T avait.

Type et ParameterizedType sont les deux interfaces. Vous pouvez fournir une instance de votre propre implémentation.

41

Depuis Gson 2.8.0, vous pouvez utiliser TypeToken#getParameterized(Type rawType, Type... typeArguments) pour créer le TypeToken, puis getType() devrait faire l'affaire.

Par exemple:

TypeToken.getParameterized(ArrayList.class, myClass).getType()
43
oldergod

Option 1 - implémenter Java.lang.reflect.ParameterizedType toi-même et passe-le à Gson.

private static class ListParameterizedType implements ParameterizedType {

    private Type type;

    private ListParameterizedType(Type type) {
        this.type = type;
    }

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

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

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

    // implement equals method too! (as per javadoc)
}

Alors simplement:

Type type = new ListParameterizedType(clazz);
List<T> list = gson.fromJson(json, type);

Notez que selon javadoc , la méthode equals devrait également être mise en œuvre.

Option 2 - (ne le faites pas) réutilisez gson internal ...

Cela fonctionnera aussi, du moins avec Gson 2.2.4.

Type type = com.google.gson.internal.$Gson$Types.newParameterizedTypeWithOwner(null, ArrayList.class, clazz);
29
MateSzvoboda

Cela a fonctionné pour moi:

public <T> List<T> listEntity(Class<T> clazz)
        throws WsIntegracaoException {
    try {
        // Consuming remote method
        String strJson = getService().listEntity(clazz.getName());

        JsonParser parser = new JsonParser();
        JsonArray array = parser.parse(strJson).getAsJsonArray();

        List<T> lst =  new ArrayList<T>();
        for(final JsonElement json: array){
            T entity = GSON.fromJson(json, clazz);
            lst.add(entity);
        }

        return lst;

    } catch (Exception e) {
        throw new WsIntegracaoException(
                "WS method error [listEntity()]", e);
    }
}
7
Rodrigo Tavares

Vous pouvez faire cela avec le plus puissant de Guava TypeToken :

private static <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
    return new TypeToken<ArrayList<T>>() {}
            .where(new TypeParameter<T>() {}, type)
            .getType();
}
7
shmosel

Sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl workes. Pas besoin d'implémentation personnalisée

Type type = ParameterizedTypeImpl.make(List.class, new Type[]{childrenClazz}, null);
List list = gson.fromJson(json, type);

Peut être utilisé avec des cartes et toute autre collection:

ParameterizedTypeImpl.make(Map.class, new Type[]{String.class, childrenClazz}, null);

Voici une belle démonstration de la façon dont vous pouvez l’utiliser dans un désérialiseur personnalisé: Désérialisation de ImmutableList avec Gson

5
varren

Vous pouvez réellement le faire. Il vous suffit d’analyser d’abord vos données dans un JsonArray, puis de transformer chaque objet individuellement et de l’ajouter à un List:

Class<T> dataType;

//...

JsonElement root = jsonParser.parse(json);
List<T> data = new ArrayList<>();
JsonArray rootArray = root.getAsJsonArray();
for (JsonElement json : rootArray) {
    try {
        data.add(gson.fromJson(json, dataType));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
return data;
3
Ovidiu Latcu