2012-09-25 17 views
12

AFAIK la personalización gson más flexible es posible a través de TypeAdapterFactory, sin embargo, puede complicarse innecesariamente. Me obliga a escribir para cada clase manejada read y write, aunque a veces solo se necesita un método. Por otra parte, a veces un JsonSerializer y/o JsonDeserializer eran mucho más fáciles de escribir, p. like here. Esto me lleva a estas preguntas:Uso más simple de TypeAdapterFactory

  • ¿Es posible escribir TypeAdapter la cual simplemente delegados uno de sus métodos (por ejemplo, la escritura de ImmutableList a la escritura de List)?
  • ¿Es posible utilizar de alguna manera JsonSerializer y/o JsonDeserializer junto con TypeAdapterFactory? Alternativamente, ¿hay una fábrica para ellos?

Respuesta

16

Es posible crear un TypeAdapter que delegue uno de sus métodos. Este caso de uso es una parte importante de la API, y existe un método getDelegateAdapter() solo para este propósito. Pase this como el primer argumento para getDelegateAdapter que devolverá el adaptador que tiene prioridad después de la fábrica actual.

TypeAdapterFactory immutableListFactory = new TypeAdapterFactory() { 
    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 
    if (!(type.getType() instanceof ParameterizedType) 
     || !type.getRawType().equals(ImmutableList.class)) { 
     return null; 
    } 

    ParameterizedType parameterizedType = (ParameterizedType) type.getType(); 
    TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type); 
    TypeAdapter<?> elementAdapter = gson.getAdapter(
     TypeToken.get(parameterizedType.getActualTypeArguments()[0])); 
    return new ImmutableListAdapter(delegate, elementAdapter); 
    } 

    class ImmutableListAdapter<E> extends TypeAdapter<ImmutableList<E>> { 
    private TypeAdapter<List<E>> delegate; 
    private TypeAdapter<E> element; 

    ImmutableListAdapter(TypeAdapter<List<E>> delegate, TypeAdapter<E> element) { 
     this.delegate = delegate; 
     this.element = element; 
    } 

    @Override public void write(JsonWriter out, ImmutableList<E> value) throws IOException { 
     delegate.write(out, value); 
    } 

    @Override public ImmutableList<E> read(JsonReader in) throws IOException { 
     if (in.peek() == JsonToken.NULL) { 
     in.nextNull(); 
     return null; 
     } 
     ImmutableList.Builder<E> builder = ImmutableList.builder(); 
     in.beginArray(); 
     while (in.hasNext()) { 
     builder.add(element.read(in)); 
     } 
     in.endArray(); 
     return builder.build(); 
    } 
    } 
}; 

Usted puede mezclar y combinar JsonSerializer/JsonDeserializer con TypeAdapterFactory, pero no directamente. La forma más sencilla es volver a llamar a Gson para serializar valores secundarios en su clase. En este ejemplo se cambiaría el bucle interior a esto:

 while (in.hasNext()) { 
     builder.add(gson.<E>fromJson(in, elementType)); 
     } 

La principal diferencia entre JsonSerializer/JsonDeserializer y TypeAdapter es el número de etapas que se necesita para pasar de JSON para su modelo de objetos. Con JsonSerializer/JsonDeserializer los objetos se convierten primero al modelo DOM de Gson (JsonElement etc.) y luego se convierten a su modelo de objetos. Con TypeAdapter, se omite el paso intermedio.

Esto hace que el código del adaptador de tipo sea un poco más complicado de leer y escribir, por lo que debería preferirlo solo para el código optimizado.

+1

Esta es una gran respuesta. Una de las cien mejores que he leído en este sitio a lo largo de los años. ¡Gracias por tomarse el tiempo de dar un ejemplo realista! Esta respuesta cambió mi punto de vista de Gson por sí solo. – kevinarpe