2010-11-19 5 views
21

J'utilise GSON pour décoder JSON en un objet de type T par ex.Utilisation de génériques avec GSON

public T decode(String json) { 
    Gson gson = new Gson(); 
    return gson.fromJson(json, new TypeToken<T>() {}.getType()); 
} 

Cela renvoie cependant une exception -

java.lang.AssertionError: Type inattendu. prévue l'un des: java.lang.reflect.ParameterizedType, java.lang.reflect.GenericArrayType, mais a obtenu: sun.reflect.generics.reflectiveObjects.TypeVariableImpl, pour jeton de type: T

Je pense que par en utilisant TypeToken j'ai évité l'effacement de type.

Je me trompe?

Merci

+0

Question en double: http://stackoverflow.com/questions/5370768/using-a-generic-type-with-gson –

Répondre

14

Tout d'abord, je ne vois pas comment il est utile d'envelopper Gson comme ça. En ce qui concerne votre problème, les informations sur le type générique T lui-même ne sont pas disponibles pendant l'exécution. Ça a été effacé. C'est seulement disponible pendant la compilation. Vous voulez le paramétrer avec le type réel à la place comme new TypeToken<List<String>>.

En raison du manque de réifiées Generics en Java (il est impossible de faire un T t = new T()), Gson lui-même est forcé d'utiliser l'approche TypeToken, comme vous le voyez. Sinon, Gson l'aurait fait d'une manière beaucoup plus élégante.

Afin de pouvoir passer le type réel, vous devez réinventer la même chose que TypeToken fait déjà. Et cela n'a pas de sens :) Il suffit de le réutiliser ou juste d'utiliser Gson directement sans l'encapsuler dans une classe d'aide comme ça.

+1

Je détachais l'utilisation réelle de GSON du mécanisme qui l'utilise. J'ai une interface ObjectDecoder avec la méthode decode (String json). Cela signifie que je peux basculer entre différentes implémentations, par ex. GSON, Jackson etc. J'utilise ensuite Guice pour injecter la dépendance dans le mécanisme. – christophmccann

+3

Votre meilleur pari est vraiment de réutiliser le 'TypeToken' de Gson comme argument de classe ou de méthode ou au moins de le réinventer si vous ne voulez pas qu'une dépendance de tiers soit exposée dans votre API. [C'est open source avec la licence Apache] (http://code.google.com/p/google-gson/source/browse/trunk/src/main/java/com/google/gson/reflect/TypeToken.java?spec = svn2 & r = 2). [Explication d'arrière-plan ici] (http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener). – BalusC

5

Je pense que la première réponse ne pointe pas la solution réelle: vous devez également passer par exemple de classe avec T, comme ceci:

public T decode(String json, Class<T> cls) { 
    Gson gson = new Gson(); 
    return gson.fromJson(json, cls); 
} 

C'est parce que « T » est ici une variable de type, non une référence de type; et seulement utilisé par le compilateur pour ajouter des conversions implicites et vérifier la compatibilité des types. Mais si vous passez la classe réelle, vous pouvez l'utiliser; et le compilateur vérifiera la compatibilité de type pour réduire le risque de discordance.

Vous pouvez également utiliser TypeToken et le transmettre; mais TypeToken doit être construit avec un type réel, pas une variable de type; La variable de type est peu utile ici. Mais si vous voulez envelopper des choses, vous ne voudriez pas que l'appelant utilise TypeToken (qui est un type Gson). Le même mécanisme d'enrobage fonctionnerait avec d'autres bibliothèques comme Jackson, que vous avez mentionnées.

+4

Cela ne fonctionnera pas pour les classes paramétrées où cette question est tout. Il n'y a rien de tel que 'Map .class', seulement' Map.class'. Voir aussi le lien "explication de base" dans le commentaire de ma réponse. – BalusC

+0

Oui, je suis pleinement conscient de ce problème, c'est juste que la question ne suggérait pas qu'il s'agissait d'une classe paramétrée, donc une simple réponse semblait suffisante. Il est dommage que JDK n'ait pas d'équivalent de jeton de type (référence de type etc, chaque lib/framework ayant à définir ses propres). – StaxMan

0

Ma solution à c'était d'utiliser l'analyseur de JSON et le casser en morceaux

public static <TT> PushObj<TT> fromJSON(String json, Class<TT> classType) 
{ 
    JsonObject jObj = new JsonParser().parse(json).getAsJsonObject(); 
    String type = jObj.get("type").getAsString(); 
    JsonObject job = jObj.get("object").getAsJsonObject(); 
    TT obj = new Gson().fromJson(job, classType); 
    return new PushObj<TT>(type, obj); 
} 

Lorsque la structure de l'objet est: {String: Type, générique: Object}

Et les variables sont : jObj est le JSONObject de la chaîne passée en et travail est le JSONObject de l'objet générique

donc j'ai utilisé l'analyseur de JSON pour obtenir le type séparément, et de réflexion pour la objet.

Questions connexes