简体   繁体   English

更改gson中的默认枚举序列化和反序列化

[英]Change default enum serialization & deserialization in gson

I'm using Gson in a slightly "different" way and I'm wondering if the following is possible... 我正在以一种略微“不同”的方式使用Gson,我想知道以下是否可行......

I'd like to change the default serialization/deserialization format for enums so that it uses fully-qualified class names, but maintain support for the @SerializedName annotation on said enums. 我想更改枚举的默认序列化/反序列化格式,以便它使用完全限定的类名,但在所述枚举上保持对@SerializedName注释的支持。 Basically, given the following enum... 基本上,鉴于以下枚举...

package com.example;
public class MyClass {
    public enum MyEnum {

        OPTION_ONE, 

        OPTION_TWO, 

        @SerializedName("someSpecialName")
        OPTION_THREE
    }
}

I'd like the following to be true... 我想以下是真实的......

gson.toJson(MyEnum.OPTION_ONE) == "com.example.MyClass.MyEnum.OPTION_ONE"
&&
gson.toJson(MyEnum.OPTION_TWO) == "com.example.MyClass.MyEnum.OPTION_TWO"
&&
gson.toJson(MyEnum.OPTION_THREE) == "someSpecialName"

and vice-versa. 反之亦然。

(for those curious, I'm trying to build a small lib that allows me to treat android's intent's actions as enums, so that I can write switch statements instead of a bunch of ugly if-elses + string compares, and I want to support the annotation so that I can also include custom pre-existing action strings like Intent.ACTION_VIEW, etc in the same enum). (对于那些好奇的人,我正在尝试构建一个小的lib,它允许我将android的intent的动作视为枚举,这样我就可以编写switch语句而不是一堆丑陋的if-elses + string比较,我想支持注释,以便我也可以在同一个枚举中包含自定义的预先存在的动作字符串,如Intent.ACTION_VIEW等。

So would anyone know if it's possible to register a type adapter that can fall back if the @SerializedName field is present? 那么有人会知道是否可以注册一个类型适配器,如果@SerializedName字段存在,它可以回退? Would I just have to check for that annotation myself in my own TypeAdapter? 我是否只需要在自己的TypeAdapter中检查该注释?

Thanks in advance. 提前致谢。

I created pretty nice solution for this issue : 我为这个问题创建了非常好的解决方案:

package your.package.name
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Field;

public class EnumAdapterFactory implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
        Class<? super T> rawType = type.getRawType();
        if (rawType.isEnum()) {
            return new EnumTypeAdapter<T>();
        }
        return null;
    }

    public class EnumTypeAdapter<T> extends TypeAdapter<T> {

        public void write(JsonWriter out, T value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            Enum<?> realEnums = Enum.valueOf(value.getClass().asSubclass(Enum.class), value.toString());
            Field[] enumFields = realEnums.getClass().getDeclaredFields();
            out.beginObject();
            out.name("name");
            out.value(realEnums.name());
            for (Field enumField : enumFields) {
                if (enumField.isEnumConstant() || enumField.getName().equals("$VALUES")) {
                    continue;
                }
                enumField.setAccessible(true);
                try {
                    out.name(enumField.getName());
                    out.value(enumField.get(realEnums).toString());
                } catch (Throwable th) {
                    out.value("");
                }
            }
            out.endObject();
        }

        public T read(JsonReader in) throws IOException {
            return null;
        }
    }
}

and of course : 而且当然 :

new GsonBuilder().registerTypeAdapterFactory(new EnumAdapterFactory()).create();

Hope this helps ! 希望这可以帮助 !

Did some google-ing and found the source for Gson's EnumTypeAdapter and the related AdapterFactory here: https://code.google.com/p/google-gson/source/browse/trunk/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java#717 做了一些google-ing并找到了Gson的EnumTypeAdapter和相关AdapterFactory的来源: https//code.google.com/p/google-gson/source/browse/trunk/gson/src/main/java/com/谷歌/ GSON /内部/结合/ TypeAdapters.java#717

From the looks of it, I would, in fact, have to check for the @SerializedName attribute manually, but it looks pretty simple to do. 从它的外观来看,我实际上必须手动检查@SerializedName属性,但看起来很简单。 I'm planning on copying-over both the adapter and adapter factory (almost line-for-line) and modifying the default value of name (line 724) to include the full class name. 我打算复制适配器和适配器工厂(几乎逐行)并修改name的默认值(第724行)以包含完整的类名。

The resulting TypeAdapter would look something like this... 生成的TypeAdapter看起来像这样......

private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
    private final Map<String, T> nameToConstant = new HashMap<String, T>();
    private final Map<T, String> constantToName = new HashMap<T, String>();

    public EnumTypeAdapter(Class<T> classOfT) {
      try {
        String classPrefix = classOfT.getName() + ".";
        for (T constant : classOfT.getEnumConstants()) {
          String name = constant.name();
          SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
          if (annotation != null) {
            name = annotation.value();
          } else {
            name = classPrefix + name;
          }
          nameToConstant.put(name, constant);
          constantToName.put(constant, name);
        }
      } catch (NoSuchFieldException e) {
        throw new AssertionError();
      }
    }

    public T read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      return nameToConstant.get(in.nextString());
    }

    public void write(JsonWriter out, T value) throws IOException {
      out.value(value == null ? null : constantToName.get(value));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM