具有动态ArrayList项类型的Gson TypeToken


76

我有以下代码:

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

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

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

我尝试了这个:

    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....}

1
那是一个SQLException。它与您发布的代码无关。向我们展示JDBC代码。
Sotirios Delimanolis 2013年

1
@SotiriosDelimanolis不是!我只想让该TypeToken<ArrayList<T>>()代码接受动态Arraylist类型。像这样的东西:TypeToken<ArrayList<Class.forName(MyClass)>>
Amin Sh 2013年

Answers:


49

您提出的语法无效。以下

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

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

以下

new TypeToken<ArrayList<T>>() 

由于泛型(类型擦除)和反射的工作方式,因此无法实现。整个TypeToken黑客工作,因为Class#getGenericSuperclass()以下

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

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

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

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


我认为这是可能的,使用此处所示的技术:stackoverflow.com/questions/14139437/...
伯努瓦Duffez

2
@BenoitDuffez小心。您链接的答案做了不同的事情。它使用的是实际Class对象,因此Gson非常清楚知道要反序列化为哪种类型。在OP的问题中,他们希望不使用Class对象而是仅通过类型参数来执行此操作,而方法参数本身不可用。
Sotirios Delimanolis 2014年

@SotiriosDelimanolis:你是完全正确的。我以为上课是让这个工作正常的价格。但是确实,链接的答案与OP要求的有所不同。感谢您的澄清。
Benoit Duffez 2014年


29

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

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

您与代码“公共<T>”的方法有效使用Java泛型方法,这里记载:docs.oracle.com/javase/tutorial/extra/generics/methods.html
ᵔᴥᵔʔʕ

7

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

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

我已经在项目中使用了番石榴。谢谢,一切顺利。
Drunken Daddy

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


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;

try / catch内部循环效率很低。
dobrivoje

在循环内部或外部尝试/捕获对性能没有影响。
卡森·霍尔茨海默

0

完全有效的解决方案:

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

0

使用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 = "t@t.com") 
 val jsonObjectAsString = model.json()
//output : {"name" : "example", "email" : "t@t.com"}

将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" : "t@t.com"}"
 val model : Foo? = jsonObjectAsString.fromJson() 
 //or 
 val model = jsonObjectAsString.fromJson<Foo>() 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.