2010-11-19 17 views
21

Ich verwende GSON, um JSON in ein Objekt des Typs T zu dekodieren, z.Verwenden von Generics mit GSON

public T decode(String json) { 
    Gson gson = new Gson(); 
    return gson.fromJson(json, new TypeToken<T>() {}.getType()); 
} 

Dies jedoch gibt eine Ausnahme -

java.lang.AssertionError: Unerwarteter Typ. Erwartet eines von: java.lang.reflect.ParameterizedType, java.lang.reflect.GenericArrayType, bekam aber: sun.reflect.generics.reflectiveObjects.TypeVariableImpl, für Typ-Token: T

Ich dachte, dass durch Mit TypeToken habe ich Type Erasure vermieden.

Bin ich falsch?

Dank

+0

Frage: http://stackoverflow.com/questions/5370768/using-a-generic-type-with-gson –

Antwort

14

aller Erstens sehe ich nicht, wie es sinnvoll ist Gson so zu wickeln.

In Bezug auf Ihr Problem sind die Informationen zum generischen Typ T selbst während der Laufzeit nicht verfügbar. Es wurde gelöscht. Es ist nur während der Kompilierzeit verfügbar. Sie möchten es stattdessen mit dem tatsächlichen Typ wie new TypeToken<List<String>> parametrieren.

Aufgrund fehlender generierter Generics in Java (es ist nicht möglich, eine T t = new T() zu tun), ist Gson selbst gezwungen, die TypeToken Ansatz zu verwenden, wie Sie sehen. Sonst hätte Gson es viel eleganter gemacht.

Um in der Lage zu sein, den eigentlichen Typ zu übergeben, müssen Sie das Gleiche neu erfinden, wie TypeToken bereits tut. Und das macht keinen Sinn :) Einfach wiederverwenden oder einfach nur Gson verwenden, ohne es in eine Hilfsklasse einzubauen.

+1

Ich löste die tatsächliche Verwendung von GSON von dem Mechanismus, der es verwendet. Ich habe eine ObjectDecoder-Schnittstelle mit Methode decode (String json). Dies bedeutet, dass ich zwischen verschiedenen Implementierungen z. GSON, Jackson usw. Ich benutze dann Guice, um die Abhängigkeit in den Mechanismus zu injizieren. – christophmccann

+3

Am besten ist es, Gsons 'TypeToken' als Klassen- oder Methodenargument wiederzuverwenden oder zumindest neu zu erfinden, wenn Sie nicht möchten, dass in Ihrer API eine Drittanbieterabhängigkeit auftritt. [Die Open Source-Version der Apache-Lizenz] (http://code.google.com/p/google-gson/source/browse/trunk/src/main/java/com/google/gson/reflect/TypeToken.java?spec = svn2 & r = 2). [Hintergrund-Erklärung hier] (http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener). – BalusC

5

ich denke, die erste Antwort ist die aktuelle Lösung nicht aus Hinweis: Sie müssen auch Klasse Instanz zusammen mit T passieren, etwa so:

public T decode(String json, Class<T> cls) { 
    Gson gson = new Gson(); 
    return gson.fromJson(json, cls); 
} 

Dies liegt daran, ‚T‘ hier eine Variable vom Typ ist, nicht eine Typenreferenz; und nur vom Compiler verwendet, um implizite Umwandlungen hinzuzufügen und die Typkompatibilität zu überprüfen. Aber wenn Sie die tatsächliche Klasse übergeben, kann sie verwendet werden; und der Compiler prüft die Typenkompatibilität, um das Risiko einer Fehlanpassung zu verringern.

Alternativ können Sie TypeToken verwenden und weitergeben; TypToken muss jedoch mit realem Typ und nicht mit einer Typvariablen erstellt werden. Typ Variable ist hier wenig sinnvoll. Wenn Sie jedoch Dinge umbrechen möchten, die Sie nicht verwenden möchten, verwendet TypeToken (ein Gson-Typ).

Der gleiche Umhüllungsmechanismus würde mit anderen Bibliotheken wie Jackson funktionieren, die Sie erwähnt haben.

+4

Dies funktioniert nicht für parametrisierte Klassen, bei denen es um diese Frage geht. So etwas wie 'Map .class', nur' Map.class' gibt es nicht. Siehe auch den Link "Hintergrunderklärung" im Kommentar in meiner Antwort. – BalusC

+0

Ja, mir ist dieses Problem völlig bewusst, nur dass diese Frage nicht darauf hindeutet, dass es sich um eine parametrisierte Klasse handelt, daher schien eine einfache Antwort ausreichend zu sein. Es ist schade, dass JDK kein Äquivalent vom Typ token hat (type reference etc; jede lib/framework muss eine eigene definieren). – StaxMan

0

zu Meine Lösung war, die json-Parser zu verwenden und brechen sie in Stücke

public static <TT> PushObj<TT> fromJSON(String json, Class<TT> classType) 
{ 
    JsonObject jObj = new JsonParser().parse(json).getAsJsonObject(); 
    String type = jObj.get("type").getAsString(); 
    JsonObject job = jObj.get("object").getAsJsonObject(); 
    TT obj = new Gson().fromJson(job, classType); 
    return new PushObj<TT>(type, obj); 
} 

Wo die Objektstruktur ist: {String: Typ, Allgemein: Object}

Und die Variablen : jObj ist die JSONObject der Zeichenfolge in und Arbeit übergeben die JSONObject des generischen Objekts ist

i die json Parser also verwendet, um den Typ separat zu bekommen, und Reflexion für die Objekt.

Verwandte Themen