GSON-нечувствительная десериализация перечислений
У меня есть перечисление:
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
}
Тем не менее, я хотел бы оставить константы перечисления в верхнем регистре.
Я предполагаю, что мне нужно написать адаптер, но я не нашел хорошей документации или примеров.
Какое лучшее решение?
Изменить:
Я смог заставить работать JsonDeserializer. Есть ли более общий способ написать это, хотя было бы неудачно писать это каждый раз, когда есть несоответствие регистра между значениями перечисления и строками 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());
}
}
Ответы
Ответ 1
Для вас это очень близко к примеру, приведенному в Javadoc TypeAdapterFactory:
public class CaseInsensitiveEnumTypeAdapterFactory 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(toLowercase(reader.nextString()));
}
}
};
}
private String toLowercase(Object o) {
return o.toString().toLowerCase(Locale.US);
}
}
Ответ 2
Более простой способ, который я нашел (только сейчас), - использовать аннотацию @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;
}
Это был самый простой и общий способ, который я нашел для этого. Надеюсь, это поможет вам.
Ответ 3
Это довольно старый вопрос, но принятый ответ не сработал для меня, и использование @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();
Ответ 4
Теперь вы можете добавить несколько значений для @SerializedName
следующим образом:
public enum Type {
@SerializedName(value = "live", alternate = {"LIVE"})
LIVE,
@SerializedName(value = "upcoming", alternate = {"UPCOMING"})
UPCOMING,
@SerializedName(value = "replay", alternate = {"REPLAY"})
REPLAY;
}
Я думаю, что немного поздно для тебя, но я надеюсь, что это поможет кому-то еще!