Gson枚举类型序列化

4
我将使用gson在我的Java Web应用程序中,将API模型序列化为JSON对象。 我已经成功地使用了它的注释来处理版本化的模型,但是我无法序列化Enum对象。
Action.class
public enum Action {

    @SerializedName("create") CREATE,
    @SerializedName("read") READ;

}

尝试对Hashtable或使用此枚举的其他POJO进行序列化时,我会得到大写字母的字符串。

...
Gson gson = new GsonBuilder.create();
Hashtable<Action, String> table = new Hashtable<>();
table.put(Action.CREATE, "");
gson.toJson(table) // => { "CREATE" : "" } instead of { "create" : "" }
...

我做错了什么?

1
如果您有一个值为枚举的字段会发生什么?(另外,“Hashtable”几乎不应在新代码中使用;您可能想要使用“HashMap”。) - chrylis -cautiouslyoptimistic-
将枚举用作另一个类的字段是可行的。序列化函数会将枚举转换为小写。顺便说一下,我正在处理现有代码,因此将哈希表更改为哈希映射可能会破坏某些代码,所以我现在不会碰它。 - itaied
1
听起来好像使用@SerializedName作为映射键存在特定的问题? - chrylis -cautiouslyoptimistic-
可能是小写枚举Gson的重复问题。 - Lyubomyr Shaydariv
如果在反射类型适配器(用于序列化和反序列化POJO)的作用域内使用enum@SerializedName,那么它们将能够正常工作。但是据我所知,enum@SerializedName将出现在JSON对象的值中而不是键中。 - Lyubomyr Shaydariv
显示剩余5条评论
2个回答

4

2
除了对您问题的评论(即MapTypeAdapterFactory不支持@SerializedName),说明它为什么不能用于映射,并建议在可能的情况下更改数据交换模型外,如果您确实必须使用像MapHashtable这样的数据包,您可以使用自定义的JsonSerializer
private static final TypeToken<?> actionToUnknownHashtableTypeToken = new TypeToken<Hashtable<Action, ?>>() {
};

请注意,类型标记用于更精确地定位可序列化的枚举,并缓存内部序列化名称映射:
final Gson gson = new GsonBuilder()
        .registerTypeAdapter(actionToUnknownHashtableTypeToken.getType(), getSerializedNameEnumHashtableJsonSerializer(Action.class))
        .create();
final Hashtable<Action, String> table = new Hashtable<>();
table.put(CREATE, "item");
out.println(gson.toJson(table, actionToUnknownHashtableTypeToken.getType()));

请注意toJson方法的使用。因此,您可以单独注册每个您想使其意识到@SerializedName的特定枚举。而序列化器本身:
final class SerializedNameEnumHashtableJsonSerializer<K extends Enum<K>, V>
        implements JsonSerializer<Hashtable<K, V>> {

    private final Map<K, String> serializedNames;

    private SerializedNameEnumHashtableJsonSerializer(final Map<K, String> serializedNames) {
        this.serializedNames = serializedNames;
    }

    static <K extends Enum<K>, V> JsonSerializer<Hashtable<K, V>> getSerializedNameEnumHashtableJsonSerializer(final Class<K> enumClass) {
        try {
            final Map<K, String> serializedNames = new HashMap<>();
            for ( final K enumConstant : enumClass.getEnumConstants() ) {
                final String enumName = enumConstant.name();
                final Field field = enumClass.getField(enumName);
                final SerializedName serializedName = field.getAnnotation(SerializedName.class);
                if ( serializedName != null ) {
                    serializedNames.put(enumConstant, serializedName.value());
                }
            }
            return new SerializedNameEnumHashtableJsonSerializer<>(unmodifiableMap(serializedNames));
        } catch ( final NoSuchFieldException ex ) {
            throw new AssertionError(ex);
        }
    }

    @Override
    public JsonElement serialize(final Hashtable<K, V> hashtable, final Type type, final JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();
        for ( final Entry<K, V> e : hashtable.entrySet() ) {
            final K key = e.getKey();
            final String nameCandidate = serializedNames.get(key);
            final String serializedName = nameCandidate != null ? nameCandidate : key.name();
            jsonObject.add(serializedName, context.serialize(e.getValue()));
        }
        return jsonObject;
    }

}

请注意以上序列化器仅处理SerializedName.value,并不关心SerializedName.alternate。输出结果为:

{"create":"item"}

请注意,这仅适用于键,不影响在序列化映射中作为值的枚举可能出现的@SerializedName。上面的示例也可以重构为自动处理任何枚举,而不需要每个这样的映射或哈希表在GsonBuilder中注册(可能使用缓存机制在每个serialize方法中重新分析@SerializedName注释),但这是您需要做出的设计选择。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接