2014-12-02 6 views
35

Ich habe Probleme mit dem Abrufen eines Objekts aus einer JSON-Zeichenfolge.com.google.gson.internal.LinkedTreeMap kann nicht in meine Klasse umgewandelt werden

bekam ich die Klasse 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); 
    } 
}  

Von meinem Server ich eine ArrayList<Product> in JSON String-Darstellung erhalten. Zum Beispiel:

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

Dieser String wird wie folgt generiert:

public static <T> String arrayToString(ArrayList<T> list) { 
    Gson g = new Gson(); 
    return g.toJson(list); 
} 

Zu meinem Objekt wieder verwende ich diese Funktion:

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

Aber wenn Sie anrufen

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

Ich bekomme den Fehler com.google.gson.internal.LinkedTreeMap kann nicht in Objekt umgewandelt werden.Produkt

Was mache ich falsch? Es sieht so aus, als hätte es eine Liste von LinkedTreeMaps erstellt, aber wie konvertiere ich diese in mein Product Object?

Antwort

68

Meiner Meinung nach kann der Parser aufgrund der Typlöschung den echten Typ T zur Laufzeit nicht abrufen. Eine Abhilfe könnte darin bestehen, der Methode den Klassentyp als Parameter zur Verfügung zu stellen.

So etwas funktioniert, es gibt sicherlich andere mögliche Problemumgehungen, aber ich finde diese sehr klar und prägnant.

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 
} 

Und nennen Sie es mögen:

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

Diese Antwort liefert eine Abhilfe, aber nicht das Problem der verschachtelten Generika adressieren. Siehe diese zwei Antworten für eine allgemeinere Lösung: http://stackoverflow.com/a/14506181/4745878 http://stackoverflow.com/a/26203635/4745878 –

+3

Diese Problemumgehung ist besser als echte Lösung. Danke Alexis C. – pcejrowski

11

Ich hatte auch Probleme mit Gson beschweren sich über LinkedTreeMaps Gießen.

Die answer von Alexis und die comment von Aljoscha erklärt, warum der Fehler auftritt; "Generics auf einem Typ werden normalerweise zur Laufzeit gelöscht." Mein Problem war, dass mein Code funktionierte, wenn ich ihn normal ausführte, aber die Verwendung von ProGuard führte dazu, dass der Code entfernt wurde, der für das Casting unerlässlich war.

Sie können Alexis 'Antwort folgen und den Cast klarer definieren und das sollte die Probleme beheben. Sie können auch den von Google angegebenen ProGuard rules hinzufügen (das Problem wurde dadurch einfach gelöst).

Moral der Geschichte: Überprüfen Sie immer, was ProGuard Regeln Sie brauchen.

+0

Ich musste meine benutzerdefinierte Basisantwort abstrakte Klasse definieren, um zu behalten. '-Keep-Klasse * erweitert in.krsh.app.webservice.BaseResponseBody'. Die Klasse ist wie 'interne abstrakte Klasse BaseResponseBody {} '. – Krish

1

Ich auch konfrontiert Klassenausnahme Ausnahme von com.google.gson.internal.LinkedTreeMap nur für meine signierte Build. Ich habe diese Zeilen im Progrard hinzugefügt. Dann funktioniert es gut.

-keepattributes Signatur

-keepattributes Annotation

-keep Klasse com.google.** {*; }

-halteklasse sun.misc. ** {*; }

+3

Bitte fügen Sie weitere Informationen hinzu und erläutern Sie die Lösung. –

+0

Sie können dies in Ihrem Proguard hinzufügen und wird gut mit signierten Build funktionieren – user1645960

+1

Das sind extrem breite Proguard-Regeln. Regeln, die große Teile des Codes behalten, haben den Zweck von Proguard zunichte gemacht, da sie den ungenutzten Code in diesen Paketen nicht entfernen können, was der ganze Grund ist, warum Sie Proguard benutzen. Die Keep-Anweisungen sollten nur bis zu der spezifischen Klasse reichen, die Sie benötigen, anstatt alle von com.google. ** und sun.misc. ** zu behalten. Die korrekte Regel ist etwa wie "-keep class com.google.api.services.vision.v1.model.BatchAnnotateImagesResponse {;}", abhängig davon, wo die von Ihnen verwendete GSON-Modellklasse gespeichert ist. – OldSchool4664

-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

Ich verstehe es nicht ... –

+0

Bitte geben Sie eine Erklärung und mögliche Lösungen für das Problem. –

0

auf die Antworten Um bereits hier erwähnt, wenn Sie eine generische Klasse, die, sagen wir, HTTP-Aufrufe, es vielleicht nützlich passieren Class<T> als Teil des Konstruktor behandelt.

Um ein wenig mehr Details zu geben, geschieht dies, weil Java Class<T> während der Laufzeit mit nur T nicht schließen kann. Es benötigt die tatsächliche feste Klasse, um die Bestimmung zu treffen.

Also, wenn Sie etwas davon haben, wie ich es tue:

class HttpEndpoint<T> implements IEndpoint<T> 

können Sie die Vererbungscode erlauben auch die class<T> zu schicken, an diesem Punkt, da ist es klar, was T ist.

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

Vererbungsklasse:

public class Players extends HttpEndpoint<Player> { 

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

während nicht ganz eine saubere Lösung, es ist der Code verpackt up nicht halten, und Sie müssen nicht zu Class<T> zwischen den Methoden.

0

Ähnlich wie Alexis C Antworten. aber in Kotlin.
Übergeben Sie einfach den Klassentyp in Funktion und klären Sie, welcher generische Typ ist.
Hier ist ein vereinfachtes Beispiel.

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

Es folgt ein Beispielaufruf

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) 
} 
Verwandte Themen