【问题标题】:GSON Case-Insensitive Enum DeserializationGSON 不区分大小写的枚举反序列化
【发布时间】:2012-02-22 06:43:36
【问题描述】:

我有一个枚举:

enum Type {
    LIVE, UPCOMING, REPLAY
}

还有一些 JSON:

{
    "type": "live"
}

还有一个班级:

class Event {
    Type type;
}

当我尝试使用 GSON 反序列化 JSON 时,我收到 nullEvent 类型字段,因为 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());
    }
}

【问题讨论】:

    标签: java gson json-deserialization


    【解决方案1】:

    为您方便,这与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);
      }
    }
    

    【讨论】:

    • 请注意,上面的代码序列化小写枚举值和反序列化正确只有小写值的完全匹配。要执行不区分大小写的反序列化,请修改 read() 方法:return lowercaseToConstant.get(toLowercase(reader.nextString()))。并且要使用原始案例进行序列化,请修改 write() 方法:out.value(value.toString())
    【解决方案2】:

    我发现(刚刚)的一种更简单的方法是使用@SerializedName 注释。我在这里的EnumTest.java 中找到了它(ln 195 附近的Gender 类):

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

    这是我发现的最简单、最通用的方法。希望对你有帮助。

    【讨论】:

    • 如果我能给你更多的积分......或者喜欢,啤酒或棒棒糖。
    • 这真是太好了。以整个 android 团队的名义:谢谢 :)
    • 希望我以前知道这件事!
    • 是的,它看起来很简单,但它并不能避免字符串的冗余重复。我希望 Gson 有类似于 LOWER_CASE_WITH_UNDERSCORES 枚举字段命名策略的东西......
    • 它简洁、清晰且有用,但是,它并不是真正的“不区分大小写”,不是吗?因为它只能解析小写的item,所以遇到“liVE”之类的东西也解析不出来。
    【解决方案3】:

    现在您可以像这样为@SerializedName 添加多个值:

    public enum Type {
        @SerializedName(value = "live", alternate = {"LIVE"})
        LIVE,
    
        @SerializedName(value = "upcoming", alternate = {"UPCOMING"})
        UPCOMING,
    
        @SerializedName(value = "replay", alternate = {"REPLAY"})
        REPLAY;
    }
    

    我认为这对你来说有点晚了,但我希望它可以帮助其他人!

    【讨论】:

      【解决方案4】:

      这是一个相当老的问题,但接受的答案对我不起作用,使用@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();
      

      【讨论】:

      • 这确实实现了“不区分大小写”。它应该是公认的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-18
      • 2015-10-27
      • 1970-01-01
      • 1970-01-01
      • 2020-03-28
      • 2016-04-06
      相关资源
      最近更新 更多