type fromjson from java generics gson

fromjson - Java Type Generic como argumento para GSON



java gson to list (8)

En GSON para obtener una lista de objetos que haces

Gson gson = new Gson(); Type token = new TypeToken<List<MyType>>(){}.getType(); return gson.fromJson(json, token);

Funciona muy bien, pero quiero ir más allá y tener MyType parametrizado para que pueda tener una función común para analizar la lista de objetos con este código

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

Tristemente devuelve una matriz de StringMaps, no el tipo. T se interpreta como otro tipo genérico, no mi tipo. Cualquier solución?


En kotlin uso simple por ejemplo:

Obtener la función de lugares

fun getPlaces<T> (jsonString : String, clazz: Class<T>) : T { val places : T = Gson().fromJson(jsonString,clazz) return places }

Entonces puedes usar como:

val places = getPlaces(Array<Place>::class.java)


Esto ha sido respondido en preguntas anteriores. Básicamente, hay 2 opciones:

  1. Pase el Type desde el sitio que llama. El código de llamada usará TypeToken o lo que sea para construirlo.
  2. Construya un Type correspondiente al tipo parametrizado usted mismo. Esto requerirá que escribas una clase que implemente ParameterizedType

He llevado Raffaele''s enfoque Raffaele''s un paso más allá y he generalizado la clase, de modo que funciona con todas las clases A, donde B es una clase no parametrizada. Puede ser útil para Conjuntos y otras Colecciones.

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


La solución Kotlin "ListOfSomething" que funcionó para mí:

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


Los genéricos funcionan en tiempo de compilación. La razón por la cual los tokens de tipo súper funcionan, se debe a que las clases internas (anónimas) pueden acceder a los argumentos tipo para sus superclases genéricas (superinterfaces), que a su vez se almacenan directamente en los metadatos de bytecode.

Una vez que se compila el archivo fuente .java, obviamente se descarta el parámetro de tipo <T> . Como no se conoce en tiempo de compilación, no se puede almacenar en bytecode, por lo que se borra y Gson no puede leerlo.

ACTUALIZAR

Después de la respuesta de newacct, traté de implementar lo que él sugirió en su opción 2, es decir, implementar un ParameterizedType . El código se ve así (aquí hay una test básica):

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

el propósito de este código, se debe usar dentro de getFromJsonList() :

public List<T> fromJsonList(String json, Class<T> klass) { Gson gson = new Gson(); return gson.fromJson(json, new ListOfSomething<T>(klass)); }

Incluso si la técnica funciona y es realmente muy inteligente (no lo sabía y nunca lo hubiera pensado), este es el logro final:

List<Integer> list = new Factory<Integer>() .getFromJsonList(text, Integer.class)

en lugar de

List<Integer> list = new Gson().fromJson(text, new TypeToken<List<Integer>>(){}.getType());

Para mí, todo esto es inútil, incluso si estoy de acuerdo con que TypeToken haga que el código se vea desagradable: P


Si programamos en kotlin, podemos usar el reified type parameter en la función en línea

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

Y utilícelos a continuación en su código

val arrayList = GenericGson.fromJsonTokenType<ArrayList<Person>>(json)



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

Ejemplo:

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