2014-12-02 130 views
35

我从使用JSON字符串获取对象时遇到了一些问题。com.google.gson.internal.LinkedTreeMap无法投射到我的课堂

我得到了类Product

public class Product { 
    private String mBarcode; 
    private String mName; 
    private String mPrice; 

    public Product(String barcode, String name, String price) { 
     mBarcode = barcode; 
     mName = name; 
     mPrice = price; 
    } 

    public int getBarcode() { 
     return Integer.parseInt(mBarcode); 
    } 

    public String getName() { 
     return mName; 
    } 

    public double getPrice() { 
     return Double.parseDouble(mPrice); 
    } 
}  

从我的服务器我在JSON字符串表示得到一个ArrayList<Product>。例如:

[{"mBarcode":"123","mName":"Apfel","mPrice":"2.7"}, 
{"mBarcode":"456","mName":"Pfirsich","mPrice":"1.1111"}, 
{"mBarcode":"89325982","mName":"Birne","mPrice":"1.5555"}] 

此字符串是这样产生的:

​​

为了让我的对象后,我用这个函数:

public static <T> ArrayList<T> stringToArray(String s) { 
    Gson g = new Gson(); 
    Type listType = new TypeToken<ArrayList<T>>(){}.getType(); 
    ArrayList<T> list = g.fromJson(s, listType); 
    return list; 
} 

但是打电话时

String name = Util.stringToArray(message).get(i).getName(); 

我收到错误 com.google.gson.internal.LinkedTreeMap不能转换为object.Product

我在做什么错了?它看起来像创建了LinkedTreeMaps列表,但我如何将它们转换为我的产品对象?

回答

68

在我看来,由于类型擦除,解析器无法在运行时获取真实类型T.一种解决方法是将类类型作为参数提供给方法。

像这样的工作,肯定有其他可能的解决方法,但我觉得这个非常清晰和简洁。

public static <T> List<T> stringToArray(String s, Class<T[]> clazz) { 
    T[] arr = new Gson().fromJson(s, clazz); 
    return Arrays.asList(arr); //or return Arrays.asList(new Gson().fromJson(s, clazz)); for a one-liner 
} 

,并调用它像:

String name = stringToArray(message, Product[].class).get(0).getName(); 
+0

这个答案提供了一个解决办法,但是没有解决嵌套泛型的问题。请参阅这两个答案以获得更一般的解决方案: http://stackoverflow.com/a/14506181/4745878 http://stackoverflow.com/a/26203635/4745878 – 2016-04-01 21:26:20

+3

此解决方法比真正的解决方案更好。谢谢亚历克西斯C. – pcejrowski 2016-11-13 18:54:46

11

我还与GSON抱怨铸造LinkedTreeMaps问题。

Alexis提供的answer和Aljoscha提供的comment解释了错误发生的原因; “类型上的泛型通常在运行时被删除。”我的问题是我的代码在我正常运行时工作,但使用ProGuard会导致代码被剥离,这对于投射非常重要。

你可以按照亚历克西斯的答案,更清楚地定义演员,并应该解决问题。您也可以添加Google提供的ProGuard rules(只需执行此操作即可解决问题)。

##---------------Begin: proguard configuration for Gson ---------- 
# Gson uses generic type information stored in a class file when working with fields. Proguard 
# removes such information by default, so configure it to keep all of it. 
-keepattributes Signature 

# For using GSON @Expose annotation 
-keepattributes *Annotation* 

# Gson specific classes 
-keep class sun.misc.Unsafe { *; } 
#-keep class com.google.gson.stream.** { *; } 

# Application classes that will be serialized/deserialized over Gson 
-keep class com.google.gson.examples.android.model.** { *; } 

##---------------End: proguard configuration for Gson ---------- 

道德故事:总是检查看看你需要什么ProGuard规则。

+0

我不得不定义我的自定义基地响应抽象类来保持。 '-keep class *扩展in.krsh.app.webservice.BaseResponseBody'。这个类就像'内部抽象类BaseResponseBody {}'。 – Krish 2017-10-06 19:01:03

1

我还面临com.google.gson.internal.LinkedTreeMap的类转换异常,仅用于我的签名版本。我在progurard中添加了这些行。然后它工作正常。

-keepattributes签名

-keepattributes 注释

-keep类com.google。** {*; }

-keep class sun.misc。** {*; }

+3

请添加更多信息并解释解决方案。 – 2016-07-28 14:16:13

+0

你可以在你的proguard中添加这个,并且可以在签名版本 – user1645960 2016-07-28 16:05:31

+1

下正常工作。这些是非常广泛的Proguard规则。保留大量代码的规则破坏了Proguard的用途,因为它不能去除这些包中未使用的代码,这是您使用Proguard的全部原因。 keep语句应该一直贯彻到您需要的特定类中,而不是保留所有的com.google。**和sun.misc。**。正确的规则类似于“-keep class com.google.api.services.vision.v1.model.BatchAnnotateImagesResponse {{}}”,这取决于您所使用的GSON模型类的存储位置。 – OldSchool4664 2016-11-01 00:06:08

-1
{"root": 
[ 
    {"mBarcode":"123","mName":"Apfel","mPrice":"2.7"}, 
    {"mBarcode":"456","mName":"Pfirsich","mPrice":"1.1111"}, 
    {"mBarcode":"89325982","mName":"Birne","mPrice":"1.5555"} 
] 
} 


JsonObject root = g.fromJson(json, JsonObject.class); 
//read root element which contains list 
JsonElement e = root.get("root"); 
//as the element is array convert it 
JsonArray ja = e.getAsJsonArray(); 

for(JsonElement j : ja){ 
    //here use the json to parse into your custom object 
} 
+0

我不明白... – 2017-04-13 17:37:56

+0

请包括对问题的解释和可能的解决方案。 – 2017-08-03 20:45:44

0

要添加到这里已经提到的答案,如果你有一个处理,也就是说,HTTP调用一个通用类,它也许有用传递Class<T>作为构造函数的一部分。

为了给出更多的细节,发生这种情况是因为Java在运行时不能推断出Class<T>,只有T。它需要实际的固体课来做出决定。

所以,如果你有这样的事情,像我这样做:

class HttpEndpoint<T> implements IEndpoint<T> 

您可以允许继承代码也送class<T>,因为在这一点上是为清楚什么吨。

public class Players extends HttpEndpoint<Player> { 

    public Players() { 
     super("http://127.0.0.1:8080", "/players", Player.class); 
    } 
} 

,而并不完全是一个干净的解决方案,但它保持代码封装起来,你不必方法之间Class<T>

public HttpEndpoint(String baseurl, String route, Class<T> cls) { 
    this.baseurl = baseurl; 
    this.route = route; 
    this.cls = cls; 
} 

继承类。

0

与Alexis C的回答相似。但在Kotlin。
只需将类类型传入函数并明确哪些泛型是。
这里是简化的例子。

inline fun <reified T> parseArray(json: String, typeToken: Type): T { 
    val gson = GsonBuilder().create() 
    return gson.fromJson<T>(json, typeToken) 
} 

下面是示例调用

fun test() { 
    val json: String = "......." 
    val type = object : TypeToken<List<MyObject>>() {}.type 
    val result: List<MyObject> = parseArray<List<MyObject>>(json = json, typeToken = type) 
    println(result) 
}