GSON не регистрозависимая десериализация перечислений - PullRequest
27 голосов
/ 30 января 2012

У меня есть 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
}

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

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

Какое лучшее решение?


Редактировать:

Мне удалось заставить работать 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());
    }
}

Ответы [ 4 ]

89 голосов
/ 20 августа 2013

Более простой способ, который я нашел (только сейчас), - использовать аннотацию @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;
}

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

19 голосов
/ 23 марта 2012

Удобно для вас, это очень близко к примеру, приведенному в Javadoc TypeAdapterFactory's :

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);
  }
}
10 голосов
/ 17 января 2017

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

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

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

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

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

10 голосов
/ 28 октября 2015

Это довольно старый вопрос, но принятый ответ у меня не сработал, и использования @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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...