web-dev-qa-db-ja.com

GSONの大文字と小文字を区別しない列挙型の逆シリアル化

私は列挙型を持っています:

enum Type {
    LIVE, UPCOMING, REPLAY
}

そしていくつかのJSON:

{
    "type": "live"
}

そしてクラス:

class Event {
    Type type;
}

GSONを使用してJSONを逆シリアル化しようとすると、JSONの型フィールドの大文字と小文字が列挙型の大文字と一致しないため、null型フィールドにEventが返されます。

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

便利なことに、これは TypeAdapterFactoryの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);
  }
}
20
Jesse Wilson

これを行うために私が(今)見つけたより簡単な方法は、@SerializedNameアノテーションを使用することです。私はそれをここのEnumTest.Java(ln195あたりのGenderクラス)で見つけました:

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

これは、すべてのタイプが「大文字と小文字を区別しない」のではなく、小文字で入力されることを前提としています。

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

    @SerializedName("upcoming")
    UPCOMING,

    @SerializedName("replay")
    REPLAY;
}

これは、私がこれを行うために見つけた最も単純で最も一般的な方法でした。それがあなたを助けることを願っています。

90
prodaea

これはかなり古い質問ですが、受け入れられた回答は私には機能しませんでした。@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();
11
jbarguil

これで、次のように@SerializedNameに複数の値を追加できます。

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

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

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

少し遅いと思いますが、他の人の役に立つことを願っています!

10
Blunderer