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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20773850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:15:14  来源:igfitidea点击:

Gson TypeToken with dynamic ArrayList item type

javareflectionarraylistgson

提问by Amin Sh

I have this code:

我有这个代码:

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

It converts a JSON string to a Listof objects. But now I want to have this ArrayListwith a dynamic type (not just myClass), defined at runtime.

它将一个 JSON 字符串转换为一个List对象。但是现在我想要在运行时定义ArrayList的动态类型(不仅仅是myClass)。

The ArrayList's item type will be defined with reflection.

所述ArrayList的项目类型将与被定义反射

I tried this:

我试过这个:

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

But it doesn't work. This is the exception:

但它不起作用。这是例外:

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

采纳答案by Sotirios Delimanolis

The syntax you are proposing is invalid. The following

您提议的语法无效。下列

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

is invalid because you're trying to pass a method invocation where a type name is expected.

无效,因为您正在尝试传递需要类型名称的方法调用。

The following

下列

new TypeToken<ArrayList<T>>() 

is not possible because of how generics (type erasure) and reflection works. The whole TypeTokenhack works because Class#getGenericSuperclass()does the following

由于泛型(类型擦除)和反射的工作原理,这是不可能的。整个TypeTokenhack 之所以有效,是因为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.

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

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

In other words, if it sees ArrayList<T>, that's the ParameterizedTypeit will return and you won't be able to extract the compile time value that the type variable Twould have had.

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

Typeand ParameterizedTypeare both interfaces. You can provide an instance of your own implementation.

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

回答by Rodrigo Tavares

This worked for me:

这对我有用:

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

回答by MateSzvoboda

Option 1- implement java.lang.reflect.ParameterizedTypeyourself and pass it to Gson.

选项 1- 实现java.lang.reflect.ParameterizedType自己并将其传递给 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)
}

Then simply:

然后简单地:

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

Note that as per javadoc, equals method should also be implemented.

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

Option 2- (don't do this) reuse gson internal...

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

This will work too, at least with Gson 2.2.4.

这也适用,至少在 Gson 2.2.4 中如此。

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

回答by Ovidiu Latcu

You can actually do it. You just need to parse first your data into an JsonArrayand then transform each object individually, and add it to a List:

你实际上可以做到。您只需要首先将数据解析为JsonArray,然后单独转换每个对象,并将其添加到List

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;

回答by varren

sun.reflect.generics.reflectiveObjects.ParameterizedTypeImplworkes. No need for custom implementation

sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl工作。无需自定义实现

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

Can be used with maps and any other collection:

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

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

Here is nice demo how you can use it in custom deserializer: Deserializing ImmutableList using Gson

这是一个很好的演示如何在自定义反序列化器中使用它: Deserializing ImmutableList using Gson

回答by shmosel

You can do this with Guava's more powerful TypeToken:

你可以用 Guava 的更强大的来做到这一点TypeToken

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

回答by oldergod

Since Gson 2.8.0, you can use TypeToken#getParameterized(Type rawType, Type... typeArguments)to create the TypeToken, then getType()should do the trick.

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

For example:

例如:

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

回答by TheLogicGuy

Fully working solution:

完全有效的解决方案:

String json = .... //example: mPrefs.getString("list", "");
ArrayList<YouClassName> myTypes = fromJSonList(json, YouClassName.class);


public <YouClassName> ArrayList<YouClassName> fromJSonList(String json, Class<YouClassName> type) {
        Gson gson = new Gson();
        return gson.fromJson(json, TypeToken.getParameterized(ArrayList.class, type).getType());
    }