Seltsames Verhalten beim Deserialisieren verschachtelter generischer Klassen mit GSON

8

Ich schreibe eine Klasse, die eine Verbindung zu einem Server herstellt und basierend auf einigen Argumenten eine json-Zeichenfolge abruft, die mit GSON zu der angegebenen Klasse (via Generika) analysiert wird.

Eine abgespeckte Version der verantwortlichen Klasse sieht so aus:

%Vor%

(Die JSON -variable sieht so aus .)

Die Klasse, die die einmal deserialisierten Daten speichert, sieht so aus:

%Vor%

Die Klasse, für die die Daten dererialisiert werden sollen:

%Vor%

Und der Code, der ausgeführt wird, verwendet die obigen Klassen:

%Vor%

Was zu der folgenden Ausnahme führt

  

ClassCastException: com.google.gson.internal.StringMap kann nicht in sunnerberg.skolbibliotek.book.Language

umgewandelt werden

Jede Hilfe oder Vorschläge werden sehr geschätzt!

    
Zar 24.01.2013, 14:40
quelle

3 Antworten

19

Die kurze Antwort ist, dass Sie die Erstellung von TypeToken aus Executor entfernen, die T in Response<T> binden müssen, wenn Sie das Token erstellen ( new TypeToken<Response<Language>>() {} ), und den Typ übergeben Token zum Executor -Konstruktor.

Die lange Antwort lautet:

Generics auf einem Typ werden normalerweise zur Laufzeit gelöscht, außer wenn der Typ mit dem generischen Parameter gebunden ist. In diesem Fall fügt der Compiler die Informationen des generischen Typs in die kompilierte Klasse ein. In anderen Fällen ist das nicht möglich.

Betrachten wir zum Beispiel:

%Vor%

Zur Laufzeit enthält Java weiß bar enthält Ganzzahlen, da der Typ Integer zur Kompilierungszeit an ArrayList gebunden ist. Die generischen Typinformationen werden daher in der IntegerList -Klassendatei gespeichert . Die Informationen zum generischen Typ für foo werden jedoch gelöscht, sodass es zur Laufzeit nicht wirklich möglich ist zu bestimmen, dass foo Integer s enthalten soll.

So kommt es häufig vor, dass wir generische Typinformationen in einer Situation benötigen, in der sie normalerweise vor der Laufzeit gelöscht würden, wie hier im Fall der Analyse von JSON-Daten in GSON. In diesen Situationen können wir die Tatsache ausnutzen, dass Typinformationen erhalten bleiben, wenn sie zur Kompilierungszeit gebunden sind (wie im obigen Beispiel IntegerList ), indem Sie Typ-Token , die wirklich nur kleine anonyme Klassen sind, die bequem allgemeine Informationen speichern.

Nun zu Ihrem Code:

%Vor%

In dieser Zeile Ihrer Klasse Executor erstellen wir eine anonyme Klasse (die von TypeToken erbt), die bei der Kompilierung den Typ Response<T> hart codiert (gebunden) hat. Zur Laufzeit kann GSON also feststellen, dass Sie ein Objekt von Response<T> wollen. Aber es weiß nicht, was T ist, weil Sie es nicht zur Kompilierzeit angegeben haben! GSON kann also nicht bestimmen, welcher Typ sich in List des von ihm erstellten Response -Objekts befindet, und stattdessen einfach ein StringMap erstellen.

Die Moral der Geschichte ist, dass Sie T bei der Kompilierung angeben müssen. Wenn Executor allgemein verwendet werden soll, müssen Sie wahrscheinlich den Typ-Token außerhalb dieser Klasse in Ihrem Client-Code erstellen. Etwas wie:

%Vor%

Übrigens habe ich das oben auf meiner Maschine getestet.

Tut mir leid, wenn das zu lange war!

    
matts 24.01.2013, 16:33
quelle
2

Sie haben response.execute(); nach Executor<Language> executor = new Executor<Language>(); diese Anweisung nicht aufgerufen. Sie können die Java Generics hier nicht verwenden, aber Sie können den gleichen Effekt mit dem folgenden Code erhalten.

Antwort.java

%Vor%

Sprache.java

%Vor%

Zum Schluss der Executor.java

%Vor%

Sie können es wie folgt testen

%Vor%     
Visruth 24.01.2013 16:36
quelle
0

Ihr Problem ist mit java Typ löschen verbunden. Generics sind nur zur Kompilierzeit bekannt.

Leider gibt es für GSON keine Möglichkeit zu wissen, in welche Klasse es deserialisiert werden sollte, indem man reflektiert.

    
Samuel EUSTACHI 24.01.2013 16:22
quelle

Tags und Links