2014-06-09 150 views
4

我在轻微的“不同”的方式使用GSON,我想知道如果下面是可能的...更改默认枚举系列化和反序列化在GSON

我想改变缺省序列/ enums的反序列化格式,以便它使用完全限定的类名,但在所述枚举上保留对@SerializedName注解的支持。基本上,给出下面的枚举...

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

     OPTION_ONE, 

     OPTION_TWO, 

     @SerializedName("someSpecialName") 
     OPTION_THREE 
    } 
} 

我想下面的是真实的......

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" 

,反之亦然。我试图建立一个小的库,允许我将android的intent作为枚举对待,这样我就可以编写switch语句而不是一堆丑陋的if-elses +字符串比较,并且我想支持注释,以便我还可以在同一个枚举中包含像Intent.ACTION_VIEW等自定义预先存在的操作字符串)。

所以有谁知道是否可以注册一个类型适配器,如果@SerializedName字段存在可以退回?我是否必须自己在自己的TypeAdapter中检查该注释?

在此先感谢。

回答

2

做了一些谷歌-ING和发现GSON的EnumTypeAdapter源和相关AdapterFactory这里:https://code.google.com/p/google-gson/source/browse/trunk/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java#717

从外观上来看,我想,其实有来检查@SerializedName手动属性,但它看起来很简单。我打算复制适配器和适配器工厂(几乎是线对线)并修改默认值name(第724行)以包含完整的类名。

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

要离开这一点,因为有些人在没有回答的情况下,其他人有更好的答案,用较少的复制意面。 – Geoff

4

我创建了这个问题相当不错的解决方案:

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

,当然还有:

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

希望这有助于!