8

我想写一个泛型函数,用 Gson 反序列化泛型类型列表,代码如下:

private <T> List<T> GetListFromFile(String filename)
    {
        //Read textfile
        BufferedReader reader;
        String data="";
        try 
        {
            reader = new BufferedReader(new FileReader(filename));
            data = reader.readLine();
            reader.close();
        } 
        catch (FileNotFoundException ex) 
        {

        } 
        catch (IOException ex) 
        {

        }
        if (data == null) 
        {
            List<T> Spiel = new ArrayList<T>();
            return Spiel;
        }
        else
        {
            //get list with Deserialise
            Gson gson = new Gson();
            List<T> something = gson.fromJson(data, new TypeToken<List<T>>(){}.getType());
            return something;
        }
    }

但是这段代码不起作用,我得到了一个奇怪的结构,但不是我的类型的列表

当我使用时:

List<concreteType> something = gson.fromJson(data, new TypeToken<List<T>>(){}.getType());

我工作我得到一个List<concreteType>

但是我需要一个通用功能,我该如何解决?

问候 rubiktubik

4

2 回答 2

10

使用 TypeToken 的方法是行不通的。

new TypeToken<ArrayList<T>>() 

由于泛型(类型擦除)和反射的工作原理,这是不可能的。整个TypeTokenhack 之所以有效,是因为Class#getGenericSuperclass()执行以下操作

返回表示此 Class 所表示的实体(类、接口、原始类型或 void)的直接超类的 Type。

如果超类是参数化类型,则返回的 Type 对象必须准确反映源代码中使用的实际类型参数。

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

Type并且ParameterizedType都是接口。您可以提供自己的实现实例。

所以,你有两个选择:

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

private static class ListParameterizedType implements ParameterizedType {

    private Type type;

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

然后简单地说:

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

请注意,根据javadoc,还应实现 equals 方法。

选项 2:手动解析列表,然后为每个元素使用 Gson

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);
    }
}
于 2017-01-25T16:03:28.740 回答
6

如果不将T(as Class<T>) 的实际类型传递给您的方法,就无法做到这一点。

但是如果你明确地传递它,你可以创建一个TypeToken如下List<T>

private <T> List<T> GetListFromFile(String filename, Class<T> elementType) {
    ...
    TypeToken<ArrayList<T>> token = new TypeToken<ArrayList<T>>() {};
    List<T> something = gson.fromJson(data, token.getType());
    ...
}

也可以看看:

于 2013-08-19T18:56:03.193 回答