反序列化Gson中的通用列表

rub*_*bik 6 java generics serialization json gson

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

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

当我使用时:

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

我的工作我得到了List<concreteType>!!

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

关心rubiktubik

Mar*_*rte 8

使用TypeToken的方法不起作用.

new TypeToken<ArrayList<T>>() 
Run Code Online (Sandbox Code Playgroud)

由于泛型(类型擦除)和反射如何工作,因此是不可能的.整个TypeToken黑客的工作原理是因为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)
}
Run Code Online (Sandbox Code Playgroud)

那简单地说:

Type type = new ListParameterizedType(clazz);
List<T> list = gson.fromJson(json, type);
Run Code Online (Sandbox Code Playgroud)

请注意,根据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);
    }
}
Run Code Online (Sandbox Code Playgroud)


axt*_*avt 6

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

但是如果你明确地传递它,你可以创建一个TypeTokenforList<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());
    ...
}
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  • 这是行不通的。`T` 被删除,因此 `gson` 实际上并没有接收到类型信息。它只会使用默认类型作为“ArrayList”进行解析的元素类型。 (3认同)
  • 你怎么把 `com.google.common.reflect.TypeToken` 传递给 `com.google.gson.fromJson()` ? (2认同)