Dans GSON pour obtenir une liste d'objets que vous faites
Gson gson = new Gson();
Type token = new TypeToken<List<MyType>>(){}.getType();
return gson.fromJson(json, token);
Cela fonctionne très bien, mais je veux aller plus loin et paramétrer MyType afin que je puisse avoir une fonction commune pour analyser la liste d'objets avec ce code.
// the common function
public <T> List<T> fromJSonList(String json, Class<T> type) {
Gson gson = new Gson();
Type collectionType = new TypeToken<List<T>>(){}.getType();
return gson.fromJson(json, collectionType);
}
// the call
List<MyType> myTypes = parser.fromJSonList(jsonString, MyType.class);
Retourne malheureusement un tableau de StringMaps, pas le type. T est interprété comme un autre type générique, pas mon type. Une solution de contournement?
Les génériques travaillent au moment de la compilation. La raison pour laquelle les jetons de super-type fonctionnent, c'est parce que les classes internes (anonymes) peuvent accéder aux arguments de type de leurs superclasses génériques (superinterfaces), qui sont à leur tour stockées directement dans les métadonnées de code intermédiaire.
Une fois votre fichier source .Java compilé, le paramètre de type <T>
est évidemment jeté. Comme il n’est pas connu au moment de la compilation, il ne peut pas être stocké en bytecode, il est donc effacé et Gson ne peut pas le lire.
METTRE &AGRAVE; JOUR
Après la réponse de newacct, j’ai essayé de mettre en œuvre ce qu’il avait suggéré dans son option 2, c’est-à-dire de mettre en œuvre une ParameterizedType
. Le code ressemble à ceci (voici une base test ):
class ListOfSomething<X> implements ParameterizedType {
private Class<?> wrapped;
public ListOfSomething(Class<X> wrapped) {
this.wrapped = wrapped;
}
public Type[] getActualTypeArguments() {
return new Type[] {wrapped};
}
public Type getRawType() {
return List.class;
}
public Type getOwnerType() {
return null;
}
}
le but de ce code doit être utilisé dans getFromJsonList()
:
public List<T> fromJsonList(String json, Class<T> klass) {
Gson gson = new Gson();
return gson.fromJson(json, new ListOfSomething<T>(klass));
}
Même si la technique fonctionne et est très astucieuse (je ne le savais pas et je n'y aurais jamais pensé), voici le dernier exploit:
List<Integer> list = new Factory<Integer>()
.getFromJsonList(text, Integer.class)
au lieu de
List<Integer> list = new Gson().fromJson(text,
new TypeToken<List<Integer>>(){}.getType());
Pour moi, tout cela est inutile, même si je conviens que TypeToken
s rend le code méchant: P
Depuis gson 2.8.0
, vous pouvez utiliser TypeToken#getParametized((Type rawType, Type... typeArguments))
pour créer la typeToken
, alors getType()
devrait suffire.
Par exemple:
TypeToken.getParameterized(List.class, myType).getType();
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, "[{...}]");
J'ai pris un peu plus loin l'approche de Raffaele et ai généralisé la classe, de sorte qu'elle fonctionne avec toutes les classes A, où B est une classe non paramétrée. Pourrait être utile pour les ensembles et autres collections.
public class GenericOf<X, Y> implements ParameterizedType {
private final Class<X> container;
private final Class<Y> wrapped;
public GenericOf(Class<X> container, Class<Y> wrapped) {
this.container = container;
this.wrapped = wrapped;
}
public Type[] getActualTypeArguments() {
return new Type[]{wrapped};
}
public Type getRawType() {
return container;
}
public Type getOwnerType() {
return null;
}
}
Cela a été répondu dans les questions précédentes. En gros, il y a 2 options:
Type
du site appelant. Le code appelant utilisera TypeToken
ou quoi que ce soit pour le construire.Type
correspondant au type paramétré. Cela vous demandera d’écrire une classe qui implémente ParameterizedType
Si vous programmez en kotlin, nous pouvons utiliser reified type parameter
dans la fonction inline
class GenericGson {
companion object {
inline fun <reified T : Any> Gson.fromJsonTokenType(jsonString: String): T {
val type = object : TypeToken<T>() {}.type
return this.fromJson(jsonString, type)
}
inline fun <reified T : Any> Gson.fromJsonType(jsonString: String): T = this.fromJson(jsonString, T::class.Java)
inline fun <reified T : Any> fromJsonTokenType(jsonString: String): T = Gson().fromJsonTokenType(jsonString)
inline fun <reified T : Any> fromJsonType(jsonString: String): T = Gson().fromJsonType(jsonString)
}
}
Et utilisez comme ci-dessous dans votre code
val arrayList = GenericGson.fromJsonTokenType<ArrayList<Person>>(json)
Voici la base de code complète sur l'excellente réponse de @oldergod
public <T> List<T> fromJSonList(String json, Class<T> myType) {
Gson gson = new Gson();
Type collectionType = TypeToken.getParameterized(List.class, myType).getType();
return gson.fromJson(json, collectionType);
}
En utilisant
List<MyType> myTypes = parser.fromJSonList(jsonString, MyType.class);
J'espère que ça aide
En kotlin simple utiliser par exemple:
Obtient des lieux
fun getPlaces<T> (jsonString : String, clazz: Class<T>) : T {
val places : T = Gson().fromJson(jsonString,clazz)
return places
}
Ensuite, vous pouvez utiliser comme:
val places = getPlaces(Array<Place>::class.Java)
La solution "ListOfSomething" de Kotlin qui a fonctionné pour moi:
fun <T: Any> getGsonList(json: String, kclass: KClass<T>) : List<T> {
return getGsonInstance().fromJson<List<T>>(json, ListOfSomething<T>(kclass.Java))
}
internal class ListOfSomething<X>(wrapped: Class<X>) : ParameterizedType {
private val wrapped: Class<*>
init {
this.wrapped = wrapped
}
override fun getActualTypeArguments(): Array<Type> {
return arrayOf<Type>(wrapped)
}
override fun getRawType(): Type {
return ArrayList::class.Java
}
override fun getOwnerType(): Type? {
return null
}
}