it-swarm.com.de

com.google.gson.internal.LinkedTreeMap kann nicht in meine Klasse umgewandelt werden

Ich habe Probleme beim Abrufen meines Objekts aus einer JSON-Zeichenfolge. 

Ich habe die Klasse Product bekommen

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 bekomme ich einen ArrayList<Product> in JSON-String-Darstellung. 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 folgendermaßen generiert:

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

Um mein Objekt zurückzubekommen, benutze 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 beim anrufen 

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

Ich erhalte die Fehlermeldung com.google.gson.internal.LinkedTreeMap kann nicht in object.Product umgewandelt werden.

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

45
Tanzmaus

Meiner Meinung nach kann der Parser aufgrund der Typlöschung nicht zur Laufzeit den echten Typ T abrufen. Eine Problemumgehung wäre, den Klassentyp als Parameter für die Methode bereitzustellen. 

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

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 nenne es wie:

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

Ich hatte auch Probleme mit GSON, die sich über das Casting von LinkedTreeMaps beschwerten. 

Die Antwort von Alexis und der Kommentar von Aljoscha erläutert, warum der Fehler auftritt. "Generics für einen Typ werden normalerweise zur Laufzeit gelöscht." Mein Problem war, dass mein Code funktionierte, wenn ich ihn normal ausführte. Durch die Verwendung von ProGuard wurde jedoch Code entfernt, der für das Casting unerlässlich war.

Sie können Alexis 'Antwort folgen und die Besetzung deutlicher definieren, und dies sollte die Probleme beheben. Sie können auch die ProGuard rules hinzufügen, die von Google bereitgestellt werden (indem Sie einfach das Problem dadurch für mich erledigen).

##---------------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  ----------

Moral of the Story: Prüfen Sie immer, welche ProGuard-Regeln Sie benötigen.

12
welshk91

Ähnlich den Antworten von Alexis C. aber in Kotlin.
Übergeben Sie einfach den Klassentyp in function und klären Sie, was ein generischer 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)
}

Hier ist 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)
}
4
Johnny

Ich hatte auch eine Klassenausnahme von com.google.gson.internal.LinkedTreeMap nur für meinen signierten Build. Ich habe diese Zeilen in Progurard hinzugefügt. Dann funktioniert es gut.

-keepattributes Signatur

-keepattributes Anmerkung

-Kursklasse com.google. ** {*; }

-Steep-Klasse Sun.misc. ** {*; }

1
user1645960

Für JSON 

{
    results: [
    {
        id: "10",
        phone: "+91783XXXX345",
        name: "Mr Example",
        email: "[email protected]"
    },
    {
        id: "11",
        phone: "+9178XXXX66",
        name: "Mr Foo",
        email: "[email protected]"
    }],
    statusCode: "1",
    count: "2"
}

In listView BaseAdapter-Datei müssen wir Daten mithilfe des LinkedTreeMap-Schlüsselwertobjekts zuordnen, um den Zeilenattributwert wie folgt zu erhalten:

...
...

    @Override
    public View getView(final int i, View view, ViewGroup viewGroup) {
        if(view==null)
        {
            view= LayoutInflater.from(c).inflate(R.layout.listview_manage_clients,viewGroup,false);
        }

        TextView mUserName = (TextView) view.findViewById(R.id.userName);
        TextView mUserPhone = (TextView) view.findViewById(R.id.userPhone);


        Object getrow = this.users.get(i);
        LinkedTreeMap<Object,Object> t = (LinkedTreeMap) getrow;
        String name = t.get("name").toString();

        mUserName.setText("Name is "+name);
        mUserPhone.setText("Phone is "+phone);

        return view;
    }
...
...

ListView aus JSON-Daten mit Retrofit2 im Android-Beispiel

Quelllink

1
Code Spy

Wenn Sie beim Parsen Ihren eigenen ArrayList<MyObject> in gson verwenden;

Type typeMyType = new TypeToken<ArrayList<MyObject>>(){}.getType();

ArrayList<MyObject> myObject = gson.fromJson(jsonString, typeMyType)
1
murat gursel
{"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
Ozzie

Ich hatte das gleiche Problem. Ich habe bemerkt, dass es nur vorkommt, wenn Sie List als Argument haben.

Meine Lösung ist, die Liste in ein anderes Objekt zu packen:

class YourObjectList {

    private List<YourObject> items;

    // constructor, getter and setter
}

Mit diesem einzigen Objekt hatte ich keine Probleme mehr mit der Klassenausnahme.

0
kaiser

Wenn Sie über eine generische Klasse verfügen, die beispielsweise HTTP-Aufrufe verarbeitet, können Sie zu den hier bereits erwähnten Antworten hinzufügen. Es ist möglicherweise hilfreich, Class<T> als Teil des Konstruktors zu übergeben.

Um ein wenig mehr Details zu geben, geschieht dies, weil Java den Class<T> nicht zur Laufzeit mit T ableiten kann. Es braucht die tatsächliche Volumenklasse, um die Bestimmung zu treffen.

Wenn Sie so etwas wie ich haben:

class HttpEndpoint<T> implements IEndpoint<T>

sie können zulassen, dass der Vererbungscode auch den class<T> sendet, da an diesem Punkt klar ist, was T ist.

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

erbende Klasse:

public class Players extends HttpEndpoint<Player> {

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

auch wenn dies keine völlig saubere Lösung ist, bleibt der Code hochgepackt und Sie müssen nicht zwischen Methoden Class<T>.

0
Serguei Fedorov

verwenden Sie dies beim Parsen 

  public static <T> List<T> parseGsonArray(String json, Class<T[]> model) {
    return Arrays.asList(new Gson().fromJson(json, model));
}
0
MFQ