web-dev-qa-db-fra.com

com.google.gson.internal.LinkedTreeMap ne peut pas être converti dans une classe.

J'ai quelques problèmes pour obtenir mon objet à partir d'une chaîne JSON. 

J'ai eu la classe 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);
    }
}    

Sur mon serveur, je reçois un ArrayList<Product> en représentation JSON String. Par exemple:

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

Cette chaîne est générée comme ceci:

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

Pour récupérer mon objet, j'utilise cette fonction:

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

Mais en appelant 

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

Je reçois le message d'erreur com.google.gson.internal.LinkedTreeMap ne peut pas être converti en object.Product

Qu'est-ce que je fais mal? On dirait qu'il a créé une liste de LinkedTreeMaps, mais comment puis-je les convertir en mon produit?

45
Tanzmaus

À mon avis, en raison de l'effacement du type, l'analyseur ne peut pas récupérer le type réel T au moment de l'exécution. Une solution de contournement consisterait à fournir le type de classe en tant que paramètre de la méthode. 

Quelque chose comme cela fonctionne, il existe certainement d'autres solutions de contournement, mais je trouve celle-ci très claire et concise.

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
}

Et appelez ça comme:

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

J'ai également eu des problèmes avec GSON pour me plaindre du casting de LinkedTreeMaps. 

La réponse fournie par Alexis et le commentaire de Aljoscha explique pourquoi l'erreur est survenue. "Les génériques d'un type sont généralement effacés au moment de l'exécution." Mon problème était que mon code fonctionnait lorsque je l'exécutais normalement, mais l'utilisation de ProGuard a permis de supprimer le code qui était vital pour le casting.

Vous pouvez suivre la réponse d’Alexis et définir plus clairement la distribution, ce qui devrait régler les problèmes. Vous pouvez également ajouter les règles/ de ProGuard données par Google (cela ne fait que clarifier le problème pour moi).

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

Morale de l'histoire: Vérifiez toujours quelles règles ProGuard vous sont nécessaires.

12
welshk91

Similaire aux réponses d'Alexis C. mais à Kotlin.
Il suffit de passer le type de classe à la fonction et de préciser le type générique.
Voici un exemple simplifié.

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

Voici un exemple d'appel 

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

J'ai également été confronté à l'exception de distribution de classe de com.google.gson.internal.LinkedTreeMap uniquement pour ma version signée. J'ai ajouté ces lignes ci-dessous dans Progurard. Alors ça marche bien.

-keepattributes Signature

-keepattributes Annotation

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

-keep class Sun.misc. ** {*; }

1
user1645960

Pour 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"
}

Dans le fichier listView BaseAdapter, nous devons mapper les données à l'aide de l'objet LinkedTreeMap Key Value pour obtenir la valeur de l'attribut de ligne comme ci-dessous:

...
...

    @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 à partir de données JSON avec Retrofit2 dans Android

Lien source

1
Code Spy

Si vous utilisez votre propre ArrayList<MyObject> dans gson lors de l'analyse syntaxique;

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

J'ai eu le même problème. J'ai remarqué que cela ne se produit que lorsque vous avez la liste comme argument.

Ma solution consiste à envelopper la liste dans un autre objet:

class YourObjectList {

    private List<YourObject> items;

    // constructor, getter and setter
}

Avec cet objet unique, je n’avais plus de problèmes avec l’exception de conversion de classe.

0
kaiser

Pour ajouter aux réponses déjà mentionnées ici, si vous avez une classe générique qui gère, par exemple, les appels HTTP, il peut être utile de passer Class<T> dans le cadre du constructeur.

Pour donner un peu plus de détails, cela se produit car Java ne peut pas déduire le Class<T> au moment de l'exécution avec seulement T. Il a besoin de la classe solide réelle pour prendre la décision.

Donc, si vous avez quelque chose comme ça, comme moi:

class HttpEndpoint<T> implements IEndpoint<T>

vous pouvez autoriser le code héritant à envoyer également le class<T>, car à ce stade, il est clair que T est.

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

classe héritante:

public class Players extends HttpEndpoint<Player> {

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

bien que ce ne soit pas une solution entièrement propre, il conserve le code empaqueté et vous n'avez pas à Class<T> entre les méthodes.

0
Serguei Fedorov

utiliser ceci lors de l'analyse 

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