2012-01-30 14 views
22

Ho un enum:GSON non-Case Sensitive Enum deserializzazione

enum Type { 
    LIVE, UPCOMING, REPLAY 
} 

E alcuni JSON:

{ 
    "type": "live" 
} 

e una classe:

class Event { 
    Type type; 
} 

Quando provo a deserializzare il JSON, utilizzando GSON, ricevo null per il campo di tipo Event, poiché il caso del campo tipo in JSON non corrisponde a quello dell'enum.

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

Se cambio l'enum al seguente, poi tutto funziona benissimo:

enum Type { 
    live, upcoming, replay 
} 

Tuttavia, vorrei lasciare le costanti enum come tutte le lettere maiuscole.

Suppongo di dover scrivere un adattatore, ma non ho trovato alcuna documentazione o esempi validi.

Qual è la soluzione migliore?


Edit:

sono stato in grado di ottenere un JsonDeserializer lavoro. C'è un modo più generico per scrivere questo, anche se sarebbe spiacevole doverlo scrivere ogni volta che c'è un caso di mis-match tra i valori enum e le stringhe 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()); 
    } 
} 

risposta

18

comoda per voi, questo è molto vicino l'esempio fornito in 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

Si noti che il codice precedente _serializza_ un valore Enum in lettere minuscole e _deserializes_ correttamente solo per le corrispondenze esatte dei valori in lettere minuscole. Per eseguire la deserializzazione senza distinzione tra maiuscole e minuscole, modificare il metodo read(): 'return lowercaseToConstant.get (toLowercase (reader.nextString()))'. E per serializzare usando il caso originale, modificare il metodo write(): 'out.value (value.toString())'. – nivs

86

Un modo più semplice che ho trovato (solo ora) per fare questo è quello di utilizzare il @SerializedName annotazione. L'ho trovato nella EnumTest.java qui (il Gender classe intorno ln 195):

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

Ciò presuppone che tutti i tipi verranno sotto in minuscolo invece di essere "case insensitive"

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

    @SerializedName("upcoming") 
    UPCOMING, 

    @SerializedName("replay") 
    REPLAY; 
} 

Questo è stato il modo più semplice e generico che ho trovato per fare questo. Spero che ti aiuti.

+2

Se solo potessi inviarti più punti ... o simili, una birra o un lecca-lecca. – Spedge

+0

Questo è veramente pulito. Nel nome di un'intera squadra di android: thx :) – balazsbalazs

+2

questa dovrebbe essere la risposta accettata –

5

Questa è una domanda un po 'vecchia, ma la risposta accettata non ha funzionato per me, e l'utilizzo di @SerializedName non è sufficiente perché voglio fare in modo che posso abbinare "value", "Value" e "VALUE".

sono riuscito a fare un adattatore generica in base al codice registrato nella domanda:

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

E per usarlo:

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

Ora è possibile aggiungere più valori per @SerializedName come questo :

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

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

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

Penso che sia un po 'tardi per te ma spero che possa aiutare qualcun altro!

Problemi correlati