在使用 GSON 解析 JSON 时使用枚举
Posted
技术标签:
【中文标题】在使用 GSON 解析 JSON 时使用枚举【英文标题】:Using Enums while parsing JSON with GSON 【发布时间】:2012-01-02 22:07:38 【问题描述】:这与我之前在这里提出的一个问题有关
JSON parsing using Gson
我正在尝试解析相同的 JSON,但现在我稍微更改了我的类。
"lower": 20,
"upper": 40,
"delimiter": " ",
"scope": ["$title"]
我的班级现在看起来像:
public class TruncateElement
private int lower;
private int upper;
private String delimiter;
private List<AttributeScope> scope;
// getters and setters
public enum AttributeScope
TITLE("$title"),
DESCRIPTION("$description"),
private String scope;
AttributeScope(String scope)
this.scope = scope;
public String getScope()
return this.scope;
这段代码抛出异常,
com.google.gson.JsonParseException: The JsonDeserializer EnumTypeAdapter failed to deserialized json object "$title" given the type class com.amazon.seo.attribute.template.parse.data.AttributeScope
at
这个例外是可以理解的,因为根据我之前的问题的解决方案,GSON 期望 Enum 对象实际上被创建为
$title("$title"),
$description("$description");
但是由于这在语法上是不可能的,推荐的解决方案和变通方法是什么?
【问题讨论】:
【参考方案1】:我想扩展一点 NAZIK/user2724653 答案(就我而言)。这是一段Java代码:
public class Item
@SerializedName("status")
private Status currentState = null;
// other fields, getters, setters, constructor and other code...
public enum Status
@SerializedName("0")
BUY,
@SerializedName("1")
DOWNLOAD,
@SerializedName("2")
DOWNLOADING,
@SerializedName("3")
OPEN
在 json 文件中,您只有一个字段 "status": "N",
,其中 N=0,1,2,3 - 取决于状态值。这就是全部,GSON
与嵌套的 enum
类的值一起工作得很好。就我而言,我从json
数组中解析了Items
的列表:
List<Item> items = new Gson().<List<Item>>fromJson(json,
new TypeToken<List<Item>>().getType());
【讨论】:
这个答案完美解决了一切,不需要类型适配器! 当我使用 Retrofit/Gson 执行此操作时,枚举值的 SerializedName 添加了额外的引号。例如,服务器实际上接收的是"1"
,而不是简单的1
...
如果状态为 5 的 json 到达会发生什么?有没有办法定义默认值?
@DmitryBorodin 如果来自 JSON 的值与任何 SerializedName
不匹配,则枚举将默认为 null
。未知状态的默认行为可以在包装类中处理。但是,如果您需要 null
以外的“未知”表示,则需要编写自定义反序列化器或类型适配器。【参考方案2】:
来自the documentation for Gson:
Gson 为 Enums 提供默认的序列化和反序列化...如果您希望更改默认表示,可以通过 GsonBuilder.registerTypeAdapter(Type, Object) 注册类型适配器来实现。
以下是这样一种方法。
import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
public class GsonFoo
public static void main(String[] args) throws Exception
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(AttributeScope.class, new AttributeScopeDeserializer());
Gson gson = gsonBuilder.create();
TruncateElement element = gson.fromJson(new FileReader("input.json"), TruncateElement.class);
System.out.println(element.lower);
System.out.println(element.upper);
System.out.println(element.delimiter);
System.out.println(element.scope.get(0));
class AttributeScopeDeserializer implements JsonDeserializer<AttributeScope>
@Override
public AttributeScope deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
AttributeScope[] scopes = AttributeScope.values();
for (AttributeScope scope : scopes)
if (scope.scope.equals(json.getAsString()))
return scope;
return null;
class TruncateElement
int lower;
int upper;
String delimiter;
List<AttributeScope> scope;
enum AttributeScope
TITLE("$title"), DESCRIPTION("$description");
String scope;
AttributeScope(String scope)
this.scope = scope;
【讨论】:
【参考方案3】:使用注解@SerializedName
:
@SerializedName("$title")
TITLE,
@SerializedName("$description")
DESCRIPTION
【讨论】:
【参考方案4】:以下 sn-p 消除了对显式 Gson.registerTypeAdapter(...)
的需要,使用自 Gson 2.3 起可用的 @JsonAdapter(class)
注释(参见注释 pm_labs)。
@JsonAdapter(Level.Serializer.class)
public enum Level
WTF(0),
ERROR(1),
WARNING(2),
INFO(3),
DEBUG(4),
VERBOSE(5);
int levelCode;
Level(int levelCode)
this.levelCode = levelCode;
static Level getLevelByCode(int levelCode)
for (Level level : values())
if (level.levelCode == levelCode) return level;
return INFO;
static class Serializer implements JsonSerializer<Level>, JsonDeserializer<Level>
@Override
public JsonElement serialize(Level src, Type typeOfSrc, JsonSerializationContext context)
return context.serialize(src.levelCode);
@Override
public Level deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
try
return getLevelByCode(json.getAsNumber().intValue());
catch (JsonParseException e)
return INFO;
【讨论】:
请注意,此注解仅从 2.3 版开始可用:google.github.io/gson/apidocs/index.html?com/google/gson/… 小心将您的序列化器/反序列化器类添加到您的 proguard 配置中,因为它们可能会被删除(这发生在我身上)【参考方案5】:使用 GSON 2.2.2 版,枚举将被轻松编组和解组。
import com.google.gson.annotations.SerializedName;
enum AttributeScope
@SerializedName("$title")
TITLE("$title"),
@SerializedName("$description")
DESCRIPTION("$description");
private String scope;
AttributeScope(String scope)
this.scope = scope;
public String getScope()
return scope;
【讨论】:
【参考方案6】:如果你真的想使用 Enum 的序数值,你可以注册一个类型适配器工厂来覆盖 Gson 的默认工厂。
public class EnumTypeAdapter <T extends Enum<T>> extends TypeAdapter<T>
private final Map<Integer, T> nameToConstant = new HashMap<>();
private final Map<T, Integer> constantToName = new HashMap<>();
public EnumTypeAdapter(Class<T> classOfT)
for (T constant : classOfT.getEnumConstants())
Integer name = constant.ordinal();
nameToConstant.put(name, constant);
constantToName.put(constant, name);
@Override public T read(JsonReader in) throws IOException
if (in.peek() == JsonToken.NULL)
in.nextNull();
return null;
return nameToConstant.get(in.nextInt());
@Override public void write(JsonWriter out, T value) throws IOException
out.value(value == null ? null : constantToName.get(value));
public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory()
@SuppressWarnings("rawtypes", "unchecked")
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken)
Class<? super T> rawType = typeToken.getRawType();
if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class)
return null;
if (!rawType.isEnum())
rawType = rawType.getSuperclass(); // handle anonymous subclasses
return (TypeAdapter<T>) new EnumTypeAdapter(rawType);
;
然后注册工厂。
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(EnumTypeAdapter.ENUM_FACTORY)
.create();
【讨论】:
【参考方案7】:使用这个方法
GsonBuilder.enableComplexMapKeySerialization();
【讨论】:
虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高答案的长期价值。 从 gson 2.8.5 开始,为了在要用作键的枚举上使用 SerializedName 注释,这是必需的以上是关于在使用 GSON 解析 JSON 时使用枚举的主要内容,如果未能解决你的问题,请参考以下文章
当我在 GSON(使用枚举)上运行 Proguard 时应用程序崩溃
如何在json中使用Codable解析数据有键但值与枚举不匹配