谷歌gson LinkedTreeMap类投射到myclass

2022-08-31 23:46:11

我知道这个问题以前被问过。由于我在Java和Android方面的新手技能。我无法解决此问题超过一周。

我和我的一个朋友在开发一个Android项目时,有一些这样的事情。

这件事最奇怪的部分是,它只发生在我从谷歌Play商店下载和测试它的时候。不是从本地安卓工作室安装或调试模式。

这里可能有什么问题,或者这个返回列表完全错误?我的朋友说服这个代码正确返回,但从Play商店安装来看,它总是一个错误。

请建议我应该在哪里继续挖掘?

@Override
public void promiseMethod(JSONObject object) {
    if (object != null) {

        if (object.has(DO_SERVICES)) {
            vehicleDetails = new ArrayList < Object[] > (1);
            List < String > vehicleNoList = new ArrayList < String > (1);
            List < String > serviceList = new ArrayList < String > (1);
            try {
                Gson gson = new Gson();
                JSONObject jsonObj = new JSONObject(object.get(DO_SERVICES)
                    .toString());
                servDto = gson.fromJson(jsonObj.toString(),
                    ServiceDto.class);


                if (servDto.getServiceDto() instanceof List) {

                    List < DoServiceDto > doServiceList = servDto.getServiceDto();

例外情况是

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap 不能 cast to com.gaurage.dto.DoServiceDto at com.gaurage.user.User_Test_Main.promiseMethod(未知来源)


答案 1

序列化和反序列化泛型类型

当您调用json(obj)时,Gson调用obj.getClass()来获取有关要序列化的字段的信息。类似地,您通常可以在 fromJson(json, MyClass.class) 方法中传递 MyClass.class 对象。如果对象是非泛型类型,则此方法工作正常。但是,如果对象是泛型类型,则泛型类型信息会由于 Java 类型擦除而丢失。下面是一个说明这一点的示例:

class Foo<T> {  T value;}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // May not serialize foo.value correctly
gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar

上面的代码无法将值解释为 Bar 类型,因为 Gson 调用 list.getClass() 来获取其类信息,但此方法返回一个原始类 Foo.class。这意味着Gson无法知道这是Foo类型的对象,而不仅仅是普通的Foo。

可以通过为泛型类型指定正确的参数化类型来解决此问题。您可以使用 TypeToken 类来执行此操作。

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();    
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);

我有父类,它是子类,其中一些具有列表类型。像这个父类一样,我有30个文件。我是这样解决的。

Gson gson = new Gson();
JSONObject jsonObj = new JSONObject(object.get(DO_SERVICES).toString());
Type type = new TypeToken<MyDto>() {}.getType();
servDto = gson.fromJson(jsonObj.toString(),type);

最重要的是,我无法在Android工作室的本地测试中重现此错误。这个问题只会弹出,当我生成签名的apk并将应用程序发布到PlayStore时,应用程序停止了,并且报告说无法将LinkedTreeMap投射到myclass。

我很难在本地测试中重现相同的结果(包括调试模式)。


答案 2

EngineSense的答案是正确的。
但是,如果您仍然希望使用泛型,并且不想将具体类类型作为参数传递,下面是 Kotlin 中的解决方法示例。
(请注意,不能从 Java 调用具有重新定义类型参数的内联方法)。

可能不是完成工作的最有效方法,但它确实有效。


以下是在GsonUtil.kt

inline fun <reified T> fromJson(json: String): T? {
    return gson.fromJson(json, object : TypeToken<T>() {}.type)
}

fun <T> mapToObject(map: Map<String, Any?>?, type: Class<T>): T? {
    if (map == null) return null

    val json = gson.toJson(map)
    return gson.fromJson(json, type)
}

检索泛型对象的轻量级列表的方法。

inline fun <reified T: MyBaseClass> getGenericList(): List<T> {
    val json = ...

    //Must use map here because the result is a list of LinkedTreeMaps
    val list: ArrayList<Map<String, Any?>>? = GsonUtil.fromJson(json)
    //handle type erasure
    val result = list?.mapNotNull {
        GsonUtil.mapToObject(it, T::class.java)
    }

    return  result ?: listOf()
}

推荐