2012-01-30 13 views
22

tengo una enumeración:GSON-Case para no sensible Enum Deserialización

enum Type { 
    LIVE, UPCOMING, REPLAY 
} 

Y algunos JSON:

{ 
    "type": "live" 
} 

y una clase:

class Event { 
    Type type; 
} 

Cuando trato de deserializar el JSON, usando GSON, recibo null para el campo de tipo Event, ya que el caso del campo de tipo en el JSON no coincide con el de la enumeración.

Events events = new Gson().fromJson(json, Event.class); 

Si cambio de la enumeración a la siguiente, entonces todo funciona bien:

enum Type { 
    live, upcoming, replay 
} 

Sin embargo, me gustaría dejar las constantes de enumeración como mayúsculas.

Supongo que tengo que escribir un adaptador, pero no he encontrado ninguna buena documentación o ejemplos.

¿Cuál es la mejor solución?


Editar:

que era capaz de conseguir un trabajo JsonDeserializer. Sin embargo, hay una forma más genérica de escribir esto, ya que sería desafortunado tener que escribir esto cada vez que exista una desajuste entre los valores enum y las cadenas JSON.

protected static class TypeCaseInsensitiveEnumAdapter implements JsonDeserializer<Type> { 
    @Override 
    public Type deserialize(JsonElement json, java.lang.reflect.Type classOfT, JsonDeserializationContext context) 
      throws JsonParseException {   
     return Type.valueOf(json.getAsString().toUpperCase()); 
    } 
} 

Respuesta

18

bien para ti, esto está muy cerca el ejemplo dado en TypeAdapterFactory's Javadoc:

public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory { 
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 
    Class<T> rawType = (Class<T>) type.getRawType(); 
    if (!rawType.isEnum()) { 
     return null; 
    } 

    final Map<String, T> lowercaseToConstant = new HashMap<String, T>(); 
    for (T constant : rawType.getEnumConstants()) { 
     lowercaseToConstant.put(toLowercase(constant), constant); 
    } 

    return new TypeAdapter<T>() { 
     public void write(JsonWriter out, T value) throws IOException { 
     if (value == null) { 
      out.nullValue(); 
     } else { 
      out.value(toLowercase(value)); 
     } 
     } 

     public T read(JsonReader reader) throws IOException { 
     if (reader.peek() == JsonToken.NULL) { 
      reader.nextNull(); 
      return null; 
     } else { 
      return lowercaseToConstant.get(reader.nextString()); 
     } 
     } 
    }; 
    } 

    private String toLowercase(Object o) { 
    return o.toString().toLowerCase(Locale.US); 
    } 
} 
+0

Observe que el código anterior _serializa_ un valor de Enum minúscula y _deserializes_ correctamente solo para las coincidencias exactas de los valores en minúscula. Para realizar una deserialización insensible a las mayúsculas y minúsculas, modifique el método read(): 'return lowercaseToConstant.get (toLowercase (reader.nextString()))'. Y para serializar usando la caja original, modifique el método write(): 'out.value (value.toString())'. – nivs

86

Una manera más sencilla que encontré (en este momento) para hacer esto es utilizar la anotación @SerializedName. Lo encontré en el EnumTest.java aquí (la clase Gender alrededor ln 195):

https://code.google.com/p/google-gson/source/browse/trunk/gson/src/test/java/com/google/gson/functional/EnumTest.java?r=1230

Esto supone que todos sus tipos vendrán en tan minúsculas en lugar de ser "sensible a mayúsculas"

public enum Type { 
    @SerializedName("live") 
    LIVE, 

    @SerializedName("upcoming") 
    UPCOMING, 

    @SerializedName("replay") 
    REPLAY; 
} 

Esta fue la forma más simple y genérica que encontré para hacer esto. Espero que te ayude

+2

Si tan solo pudiera enviarte más puntos ... o como, una cerveza o una piruleta. – Spedge

+0

Esto es realmente limpio. En nombre de todo un equipo de android: thx :) – balazsbalazs

+2

esta debería ser la respuesta aceptada –

5

Esta es una pregunta bastante antiguo, pero la respuesta aceptada no funcionó para mí, y el uso de @SerializedName no es suficiente porque quiero asegurarme de que puedo coincidir "value", "Value" y "VALUE".

logré hacer un adaptador genérico basado en el código publicado en la pregunta:

public class UppercaseEnumAdapter implements JsonDeserializer<Enum> { 
    @Override 
    public Enum deserialize(JsonElement json, java.lang.reflect.Type type, JsonDeserializationContext context) 
      throws JsonParseException { 
     try { 
      if(type instanceof Class && ((Class<?>) type).isEnum()) 
       return Enum.valueOf((Class<Enum>) type, json.getAsString().toUpperCase()); 
      return null; 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
} 

Y utilizarlo:

GsonBuilder gsonBuilder = new GsonBuilder(); 
gsonBuilder.registerTypeAdapter(MyEnum.class, new UppercaseEnumAdapter()); 
Gson gson = gsonBuilder.create(); 
6

Ahora puede agregar varios valores para @SerializedName como esto :

public enum Type { 
    @SerializedName(value = "live", alternate = {"LIVE"}) 
    LIVE, 

    @SerializedName(value = "upcoming", alternate = {"UPCOMING"}) 
    UPCOMING, 

    @SerializedName(value = "replay", alternate = {"REPLAY"}) 
    REPLAY; 
} 

Creo que es un poco tarde para ti, pero espero que ayude a los demás.

Cuestiones relacionadas