2012-01-30 106 views
22

我有一个枚举:GSON不区分大小写的枚举反序列

enum Type { 
    LIVE, UPCOMING, REPLAY 
} 

和一些JSON:

{ 
    "type": "live" 
} 

和A类:

class Event { 
    Type type; 
} 

当我尝试反序列化JSON,使用GSON,我收到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()); 
    } 
} 

回答

18

便利对你来说,这是非常接近TypeAdapterFactory's Javadoc给出的例子:

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

请注意,上述代码仅对小写值的精确匹配才正确_serializes_小写的Enum值和_deserializes_。 要执行不区分大小写的反序列化,请修改read()方法: 'return lowercaseToConstant.get(toLowercase(reader.nextString()))''。 要使用原始大小写序列化,请修改write()方法: 'out.value(value.toString())'。 – nivs 2017-10-24 23:33:05

86

更简单的方法,我发现(刚才)要做到这一点是使用@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; 
} 

这是我发现的最简单最普通的方式。希望它可以帮助你。

+2

如果只有我可以给你更多的积分......或者像啤酒或棒棒糖。 – Spedge 2014-01-16 15:42:44

+0

这真的很整齐。以整个安卓团队的名义:thx :) – balazsbalazs 2014-01-23 11:57:08

+2

这应该是被接受的答案 – 2014-11-18 10:36:21

5

这是一个相当古老的问题,但接受的答案并不适用于我,并且使用@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(); 
6

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

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

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

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

我觉得对你有点迟,但我希望它能帮助其他人!