Gson TypeToken com tipo de item ArrayList dynamic

Eu tenho esse código:

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

Converte uma string JSON em uma List de objects. Mas agora eu quero ter este ArrayList com um tipo dynamic (não apenas myClass ), definido em tempo de execução.

O tipo de item da ArrayList será definido com reflection .

Eu tentei isso:

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

Mas isso não funciona. Esta é a exceção:

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

A syntax que você está propondo é inválida. Os seguintes

 new TypeToken> 

é inválido porque você está tentando passar uma chamada de método onde um nome de tipo é esperado.

Os seguintes

 new TypeToken>() 

não é possível por causa de como os genéricos (tipo apagamento) e reflection funcionam. O hack TypeToken todo funciona porque a Class#getGenericSuperclass() faz o seguinte

Retorna o Tipo que representa a superclass direta da entidade (class, interface, tipo primitivo ou nulo) representada por esta Classe.

Se a superclass for um tipo com parâmetros, o object Type retornado deve refletir com precisão os parâmetros de tipo reais usados ​​no código-fonte.

Em outras palavras, se ele vir ArrayList , esse é o ParameterizedType que retornará e você não poderá extrair o valor de tempo de compilation que a variável de tipo T teria.

Type e ParameterizedType são as duas interfaces. Você pode fornecer uma instância de sua própria implementação.

Desde o Gson 2.8.0, você pode usar TypeToken#getParameterized(Type rawType, Type... typeArguments) para criar o TypeToken , então getType() deve fazer o truque.

Por exemplo:

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

Opção 1 – implemente java.lang.reflect.ParameterizedType e passe-o para o 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) } 

Então simplesmente:

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

Note que, de acordo com o javadoc , o método equals também deve ser implementado.

Opção 2 – (não faça isso) reutilize o gson interno …

Isso vai funcionar também, pelo menos com o Gson 2.2.4.

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

Isso funcionou para mim:

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

Você pode fazer isso com o TypeToken mais poderoso do TypeToken :

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

sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl . Não há necessidade de implementação personalizada

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

Pode ser usado com mapas e qualquer outra coleção:

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

Aqui está uma boa demonstração de como você pode usá-lo no desserializador personalizado: Deserializando ImmutableList usando o Gson

Você pode realmente fazer isso. Você só precisa analisar primeiro seus dados em um JsonArray e depois transformar cada object individualmente, e adicioná-lo a uma 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;