2012-01-30 2 views
22

У меня есть перечисление:GSON Non-Case Sensitive Enum Десериализация

enum Type { 
    LIVE, UPCOMING, REPLAY 
} 

и некоторые JSON:

{ 
    "type": "live" 
} 

и класс:

class Event { 
    Type type; 
} 

Когда я пытаюсь десериализации JSON, используя GSON, я получаю null для поля типа Event, так как случай поля типа в JSON не соответствует значению перечисления.

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

Если изменить перечисление к следующему, то все работает отлично:

enum Type { 
    live, upcoming, replay 
} 

Однако, я хотел бы оставить константы перечислений в верхнем регистре.

Я предполагаю, что мне нужно написать адаптер, но не нашли никакой хорошей документации или примеров.

Какое оптимальное решение?


Edit:

Я был в состоянии получить JsonDeserializer работает. Есть ли более общий способ написать это, хотя, как было бы жаль, что нужно писать это каждый раз, когда есть случай неправильного совпадения значений enum и строк 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()); 
    } 
} 

ответ

18

Удобно для вас, это очень близко к примеру, приведенному в 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

Обратите внимание, что приведенный выше код _serializes_ значение нижнего регистра и _deserializes_ правильно выполняется только для точных совпадений нижнего регистра. Чтобы выполнить десериализацию, не учитывающую регистр, измените метод read(): 'return lowercaseToConstant.get (toLowercase (reader.nextString())). И для сериализации с использованием оригинального случая измените метод write(): 'out.value (value.toString())'. – nivs

86

Более простой способом я нашел (только сейчас), чтобы сделать это, чтобы использовать @SerializedName аннотации. Я нашел его в EnumTest.java здесь (Gender класса вокруг Л 195):

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

Это предполагает, что все ваши типов придет в качестве нижнего регистра, а не быть «чувствительны к регистру»

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

    @SerializedName("upcoming") 
    UPCOMING, 

    @SerializedName("replay") 
    REPLAY; 
} 

Это был самый простой и общий способ, который я нашел для этого. Надеюсь, это поможет вам.

+2

Если бы я мог послать вам больше очков ... или вроде бы, пива или леденец. – Spedge

+0

Это действительно опрятно. Во имя целой команды андроиды: thx :) – balazsbalazs

+2

это должен быть принятый ответ –

5

Это довольно старый вопрос, но принятый ответ не работает для меня, и использование @SerializedName не достаточно, потому что я хочу, чтобы убедиться, что я могу соответствовать "value", "Value" и "VALUE".

мне удалось сделать универсальный адаптер на основе кода, публикуемую в вопросе:

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

И использовать его:

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

Теперь вы можете добавить несколько значений @SerializedName, как это :

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

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

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

Я думаю, что для вас немного поздно, но я надеюсь, что это поможет кому-то еще!

Смежные вопросы