2014-12-02 96 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) 
}