Gson TypeToken con tipo de elemento ArrayList dynamic

Tengo este código:

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

Convierte una cadena JSON en una List de objetos. Pero ahora quiero tener este ArrayList con un tipo dynamic (no solo myClass ), definido en tiempo de ejecución.

El tipo de elemento ArrayList se definirá con reflexión .

Intenté esto:

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

Pero no funciona. Esta es la excepción:

 java.sql.SQLException: Fail to convert to internal representation: {....my json....} 

La syntax que propones no es válida. El seguimiento

 new TypeToken> 

no es válido porque está intentando pasar una invocación de método donde se espera un nombre de tipo.

El seguimiento

 new TypeToken>() 

no es posible debido a la forma en que funcionan los generics (borrado de tipo) y la reflexión. Todo el truco TypeToken funciona porque Class#getGenericSuperclass() hace lo siguiente

Devuelve el tipo que representa la superclase directa de la entidad (clase, interfaz, tipo primitivo o vacío) representada por esta clase.

Si la superclase es un tipo parametrizado, el objeto Type devuelto debe reflejar con precisión los parámetros de tipo reales utilizados en el código fuente.

En otras palabras, si ve ArrayList , ese es ParameterizedType , volverá y no podrá extraer el valor de tiempo de comstackción que habría tenido la variable de tipo T

Type y ParameterizedType son ambas interfaces. Puede proporcionar una instancia de su propia implementación.

Desde Gson 2.8.0, puede usar TypeToken#getParameterized(Type rawType, Type... typeArguments) para crear TypeToken , luego getType() debería hacer el truco.

Por ejemplo:

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

Opción 1 : implementar java.lang.reflect.ParameterizedType usted mismo y pasarlo a 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) } 

Entonces simplemente:

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

Tenga en cuenta que, de acuerdo con javadoc , el método equals también debe implementarse.

Opción 2 – (no haga esto) reutilizar gson interno …

Esto también funcionará, al menos con Gson 2.2.4.

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

Esto funcionó para mí:

 public  List listEntity(Class clazz) throws WsIntegracaoException { try { // Consuming remote method String strJson = getService().listEntity(clazz.getName()); JsonParser parser = new JsonParser(); JsonArray array = parser.parse(strJson).getAsJsonArray(); List lst = new ArrayList(); 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); } } 

Puedes hacer esto con TypeToken más potente de TypeToken :

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

sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl workes. Sin necesidad de implementación personalizada

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

Se puede usar con mapas y cualquier otra colección:

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

Aquí hay una buena demostración de cómo se puede usar en un deserializador personalizado: Deserializar ImmutableList usando Gson

En realidad puedes hacerlo. Solo necesita analizar primero sus datos en un JsonArray y luego transformar cada objeto individualmente, y agregarlo a una List :

 Class dataType; //... JsonElement root = jsonParser.parse(json); List 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;