Search code examples
javagsonjson-deserialization

GSON Case-Insensitive Enum Deserialization


I have an enum:

enum Type {
    LIVE, UPCOMING, REPLAY
}

And some JSON:

{
    "type": "live"
}

And a class:

class Event {
    Type type;
}

When I try to deserialize the JSON, using GSON, I receive null for the Event type field, since the case of the type field in the JSON does not match that of the enum.

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

If I change the enum to the following, then all works fine:

enum Type {
    live, upcoming, replay
}

However, I would like to leave the enum constants as all uppercase.

I'm assuming I need to write an adapter but haven't found any good documentation or examples.

What is the best solution?


Edit:

I was able to get a JsonDeserializer working. Is there a more generic way to write this though, as it would be unfortunate to have to write this each time there is a case mismatch between enum values and JSON strings.

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

Solution

  • Conveniently for you, this is very close to the example given in TypeAdapterFactory's Javadoc:

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