关于Java:具有动态ArrayList项类型的Gson TypeToken

Gson TypeToken with dynamic ArrayList item type

我有以下代码:

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

它将JSON字符串转换为对象的List
但是现在我想让此ArrayList具有动态类型(不仅仅是myClass),并且在运行时定义。

ArrayList的项目类型将通过反射进行定义。

我尝试了这个:

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

但这是行不通的。 这是例外:

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


从Gson 2.8.0开始,您可以使用TypeToken#getParameterized(Type rawType, Type... typeArguments)创建TypeToken,然后getType()应该可以解决问题。

例如:

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


您提出的语法无效。下列

1
new TypeToken<ArrayList<Class.forName(MyClass)>>

之所以无效,是因为您尝试传递期望类型名称的方法调用。

下列

1
new TypeToken<ArrayList< T >>()

由于泛型(类型擦除)和反射的工作方式,因此无法实现。整个TypeToken hack之所以有效,是因为Class#getGenericSuperclass()执行以下操作

Returns the Type representing the direct superclass of the entity
(class, interface, primitive type or void) represented by this Class.

If the superclass is a parameterized type, the Type object returned
must accurately reflect the actual type parameters used in the source
code.

换句话说,如果它看到ArrayList< T >,那就是它返回的ParameterizedType,您将无法提取类型变量T应当具有的编译时间值。

TypeParameterizedType都是接口。您可以提供自己的实现的实例。


选项1-自己实现java.lang.reflect.ParameterizedType并将其传递给Gson。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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)
}

然后简单地:

1
2
Type type = new ListParameterizedType(clazz);
List< T > list = gson.fromJson(json, type);

请注意,按照javadoc,equals方法也应实现。

选项2-(不要这样做)重用gson内部...

至少在Gson 2.2.4中也可以使用。

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


您可以使用番石榴功能更强大的TypeToken来做到这一点:

1
2
3
4
5
private static < T > Type setModelAndGetCorrespondingList2(Class< T > type) {
    return new TypeToken<ArrayList< T >>() {}
            .where(new TypeParameter< T >() {}, type)
            .getType();
}


这对我有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public < T > List< T > listEntity(Class< T > clazz)
        throws WsIntegracaoException {
    try {
        // Consuming remote method
        String strJson = getService().listEntity(clazz.getName());

        JsonParser parser = new JsonParser();
        JsonArray array = parser.parse(strJson).getAsJsonArray();

        List< T > lst =  new ArrayList< T >();
        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);
    }
}


sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl有效。无需自定义实施

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

可以与地图和任何其他集合一起使用:

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

这是一个不错的演示,您如何在自定义反序列化器中使用它:
使用Gson反序列化ImmutableList


您实际上可以做到。您只需要先将数据解析为JsonArray,然后分别转换每个对象,然后将其添加到List中即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class< T > dataType;

//...

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