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

问题描述 投票:0回答:14

我有这个代码:

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

它将 JSON 字符串转换为

List
对象。 但现在我想要这个
ArrayList
具有动态类型(不仅仅是
myClass
),在运行时定义。

ArrayList
的项目类型将使用reflection定义。

我试过这个:

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

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

java.sql.SQLException: Fail to convert to internal representation: {....my json....}
java reflection arraylist gson
14个回答
91
投票

从 Gson 2.8.0 开始,您可以使用

TypeToken#getParameterized(Type rawType, Type... typeArguments)
创建
TypeToken
,然后
getType()
就可以了。

例如:

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

56
投票

您建议的语法无效。以下

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

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

以下

new TypeToken<ArrayList<T>>() 
由于泛型(类型擦除)和反射的工作原理,

是不可能的。整个

TypeToken
黑客之所以有效,是因为
Class#getGenericSuperclass()
执行以下操作

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

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

换句话说,如果它看到

ArrayList<T>
,那就是它将返回的
ParameterizedType
,并且您将无法提取类型变量
T
本来具有的编译时值。

Type
ParameterizedType
都是接口。您可以提供自己的实现的实例(定义一个实现任一接口并重写其方法的类)或使用 TypeToken
 在其最新版本
中提供的
有用的工厂方法之一。例如,

private Type setModelAndGetCorrespondingList2(Class<?> typeArgument) {
    return TypeToken.getParameterized(ArrayList.class, typeArgument).getType();
}

33
投票

选项 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)
}

然后简单地说:

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

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

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

这也可以工作,至少对于 Gson 2.2.4 来说是这样。

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

9
投票

使用 kotlin,您可以使用以下函数在一行中转换(从/到)任何(JsonArray/JsonObject),而无需发送 TypeToken :-

将任何类或数组转换为 JSON 字符串

inline fun <reified T : Any> T?.json() = this?.let { Gson().toJson(this, T::class.java) }

使用示例:

 val list = listOf("1","2","3")
 val jsonArrayAsString = list.json() 
 //output : ["1","2","3"]

 val model= Foo(name = "example",email = "[email protected]") 
 val jsonObjectAsString = model.json()
//output : {"name" : "example", "email" : "[email protected]"}

将 JSON 字符串转换为任何类或数组

inline fun <reified T : Any> String?.fromJson(): T? = this?.let {
    val type = object : TypeToken<T>() {}.type
    Gson().fromJson(this, type)
}

使用示例:

 val jsonArrayAsString = "[\"1\",\"2\",\"3\"]"
 val list = jsonArrayAsString.fromJson<List<String>>()

 val jsonObjectAsString = "{ "name" : "example", "email" : "[email protected]"}"
 val model : Foo? = jsonObjectAsString.fromJson() 
 //or 
 val model = jsonObjectAsString.fromJson<Foo>() 

8
投票

这对我有用:

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

7
投票

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

TypeToken
:

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

5
投票

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

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

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

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

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


3
投票

在 Kotlin 中你可以

inline fun <reified T> parseData(row :String): T{
   return Gson().fromJson(row, object: TypeToken<T>(){}.type)
}

2
投票

你确实可以做到。您只需首先将数据解析为

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;

1
投票

完全有效的解决方案:

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

1
投票

这在 Kotlin 中是简单明了的。

val typeDataModelClass = Array<DataModelClass>::class.java

0
投票

实际上我已经制作了扩展函数来解决这个问题,将列表保存到 SharedPrefrence 并在应用程序中的任何位置检索它们,如下所示:

用它来将列表保存到 SharedPref。例如:

fun <T> SaveList(key: String?, list: List<T>?) {
val gson = Gson()
val json: String = gson.toJson(list)
getSharedPrefrences(App.getAppContext() as Application).edit().putString(key, json).apply()}

从sharedPref返回任意位置的列表。像这样:

fun Context.getList(key: String): String? {
 return getSharedPrefrences(App.getAppContext() as Application).getString(key, null)}

inline fun <reified T : Any> String?.fromJson(): T? = this?.let {
val type = object : TypeToken<T>() {}.type
Gson().fromJson(this, type)}

保存和从保存的列表中获取列表的用法如下:

saveList(Consts.SLIDERS, it.data)  

SetSliderData(this.getList(Consts.SLIDERS).fromJson<MutableList<SliderResponseItem>>()!!)

0
投票

这段代码是要在arraylist模型中转换的json字符串数据

String json = getIntent().getExtras().getString("submitQuesList", "");
Type typeOfObjectsList = new TypeToken<ArrayList<Datum_ques>>() {}.getType();
submitQuesList = new Gson().fromJson(json, typeOfObjectsList);

0
投票

我的解决方案;

JsonUtil.class

package dev.agitrubard.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.experimental.UtilityClass;

import java.util.Arrays;
import java.util.List;

@UtilityClass
public class JsonUtil {

    private static final Gson GSON = new GsonBuilder().create();

    public static <T> List<T> fromJsonArray(String jsonArray, Class<T[]> clazz) {
        return Arrays.asList(
                GSON.fromJson(jsonArray, clazz)
        );
    }

}

Main.class

public class Main {

    public static void main(String[] args) {
        String json = "[{\"id\":1,\"firstName\":\"John\",\"lastName\":\"Doe\"},{\"id\":2,\"firstName\":\"Jane\",\"lastName\":\"Doe\"}]";
        List<User> users = JsonUtil.fromJsonArray(json, User[].class);
        for (User user : users) {
            System.out.println(user.getId() + " - " + user.getFirstName() + " " + user.getLastName());
        }
    }

    @Getter
    public static class User {
        private Long id;
        private String firstName;
        private String lastName;
    }

}

Console Output

1 - John Doe
2 - Jane Doe
© www.soinside.com 2019 - 2024. All rights reserved.