2010-11-19 19 views
21

Estoy usando GSON para decodificar JSON en un objeto de tipo T, p. Ej.Uso de genéricos con GSON

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

Sin embargo, esto devuelve una excepción -

java.lang.AssertionError: un tipo inesperado. esperado uno de: java.lang.reflect.ParameterizedType, java.lang.reflect.GenericArrayType, pero obtuvo : sun.reflect.generics.reflectiveObjects.TypeVariableImpl, para Token Tipo: T

pensé que por utilizando TypeToken evité Type Erasure.

¿Estoy equivocado?

Gracias

+0

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

Respuesta

14

En primer lugar, no veo cómo es útil para envolver Gson así.

En cuanto a su problema, la información sobre el tipo genérico T en sí no está disponible durante el tiempo de ejecución. Ha sido borrado Solo está disponible durante el tiempo de compilación. Desea parametrizarlo con el tipo real en su lugar como new TypeToken<List<String>>.

Debido a la falta de Generics reificados en Java (no es posible hacer un T t = new T()), Gson se ve obligado a utilizar el enfoque TypeToken, como puede ver. De lo contrario, Gson lo habría hecho de una manera mucho más elegante.

Para poder pasar el tipo real, debe reinventar lo mismo que TypeToken ya está haciendo. Y esto no tiene sentido :) Solo reutilícelo o simplemente use Gson directamente sin envolverlo en alguna clase de ayuda de esa manera.

+1

Estaba desconectando el uso real de GSON del mecanismo que lo usa. Tengo una interfaz ObjectDecoder con método de decodificación (String json). Esto significa que puedo cambiar entre diferentes implementaciones, por ej. GSON, Jackson, etc. Luego uso Guice para inyectar la dependencia en el mecanismo. – christophmccann

+3

Su mejor apuesta es realmente reutilizar el 'TypeToken' de Gson como argumento de clase o método o al menos reinventarlo si no desea que se expone una dependencia de terceros en su API. [Es de código abierto con licencia de Apache] (http://code.google.com/p/google-gson/source/browse/trunk/src/main/java/com/google/gson/reflect/TypeToken.java?spec = svn2 & r = 2). [Explicación de fondo aquí] (http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener). – BalusC

5

Creo que la primera respuesta no está señalando la solución real: también debe pasar instancia de clase junto con T, así:

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

Esto se debe a 'T' aquí es una variable de tipo, no se una referencia de tipo; y solo lo utiliza el compilador para agregar conversiones implícitas y verificar la compatibilidad de tipos. Pero si pasas la clase real, puede usarse; y el compilador verificará la compatibilidad del tipo para reducir la posibilidad de desajuste.

Como alternativa, puede utilizar TypeToken y pasarlo; pero TypeToken debe construirse con tipo real, no con una variable de tipo; tipo de variable es de poca utilidad aquí. Pero si quiere envolver cosas, no le gustaría que la persona que llama use TypeToken (que es un tipo Gson).

El mismo mecanismo de envoltura funcionaría con otras librerías como Jackson, que usted mencionó.

+4

Esto no funcionará para las clases parametrizadas de donde se trata esta pregunta. No hay tal cosa como 'Map .class', solo' Map.class'. Ver también el enlace "explicación de fondo" en el comentario en mi respuesta. – BalusC

+0

Sí, soy plenamente consciente de este problema, es solo que esa pregunta no sugería que fuera una clase parametrizada, por lo que una respuesta simple parecía suficiente. Es una pena que JDK no tenga un equivalente de token de tipo (escriba referencia, etc., y cada lib/framework tenga que definir el suyo propio). – StaxMan

0

Mi solución a este era utilizar el analizador JSON y romperlo en pedazos

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

Cuando la estructura del objeto es: {String: Tipo, Genérico: Object}

y las variables son : jobj es la JSONObject de la cadena pasada en y trabajo es la JSONObject del objeto genérico

por lo que utiliza el analizador JSON para obtener el tipo separado, y la reflexión para la objeto.

Cuestiones relacionadas