解决Android开发中,ActiveAndroid和Gson同时使用,对象序列化失败的问题
ActiveAndroid是安卓开发常用的ORM框架。
Gson则是Google提供的轻量级序列化框架,非常适合Android开发使用。
但这两者同时使用,会产生序列化失败的问题。你通常会收到如下信息:
java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: [your model]. Forgot to register a type adapter?
这是由于ActiveAndroid框架需要模型继承com.activeandroid.Model类,而这个类貌似不能序列化,具体原因未深究,有兴趣的同学可以看看ActiveAndroid的源码。
在ActiveAndroid的Git上查阅了Issue后,发现有开发者遇到类似问题,但没有有效解决办法。
翻阅了Gson API之后,发现Google已经提供了解决途径。
假设我们有一个基础类ClassBase:
import java.io.Serializable; public class ClassBase implements Serializable { /** * -6454847757470885679L */ private static final long serialVersionUID = -6454847757470885679L; private String name; private int count; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } }
另有一个扩展类Cnblogs:
import java.io.Serializable; public class Cnblogs extends ClassBase implements Serializable { /** * 8568957562120574502L */ private static final long serialVersionUID = 8568957562120574502L; private String welcome; private int members; private String url; public String getGuid() { return welcome; } public void setGuid(String welcome) { this.welcome = welcome; } public int getMembers() { return members; } public void setMembers(int members) { this.members = members; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
如果我们在序列化时想要忽略父类的属性,怎么办呢?
这时候,你需要com.google.gson.ExclusionStrategy接口来处理这个问题。
import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; public class SpecificClassExclusionStrategy implements ExclusionStrategy { private final Class<?> excludedThisClass; private final Class<?> excludedThisClassFields; /*** * 过滤器初始化 * * @param excludedThisClass * 该类和继承自该类的对象实例将被忽略 * @param excluedThisClassFields * 该类的属性将不被序列化 */ public SpecificClassExclusionStrategy(Class<?> excludedThisClass, Class<?> excluedThisClassFields) { this.excludedThisClass = excludedThisClass; this.excludedThisClassFields = excluedThisClassFields; } @Override public boolean shouldSkipClass(Class<?> clazz) { if (clazz == null) return false; if (clazz.equals(excludedThisClass)) return true; return shouldSkipClass(clazz.getSuperclass()); } @Override public boolean shouldSkipField(FieldAttributes f) { return f.getDeclaringClass().equals(excludedThisClassFields); } }
再看看这个如何使用,
import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Main { public static void main(String[] args) { Cnblogs bl = new Cnblogs(); bl.setName("Cnblogs"); // 设置父类属性 bl.setCount(1); bl.setWelcome("代码改变世界"); // 设置当前类属性 bl.setMembers(176473); bl.setUrl("http://www.cnblogs.com");
Gson gson = new Gson(); // 通常我们使用的初始化方法 System.out.println(gson.toJson(bl));
// 使用了过滤器的初始化方法 Gson gson2 = new GsonBuilder().setExclusionStrategies(new SpecificClassExclusionStrategy(null, ClassBase.class)).create(); System.out.println(gson2.toJson(bl)); } }
两条打印语句结果如下:
{"welcome":"代码改变世界","members":2030103,"url":"http://www.cnblogs.com","name":"Cnblogs","count":1} {"welcome":"代码改变世界","members":2030103,"url":"http://www.cnblogs.com"}
因此,我们只需要在项目中,将gson实例的创建方式改为gson2,并将过滤类的构造函数第二个参数设为com.activeandroid.Model.class,序列化就成功了!
至于第一个参数,比如类A,则任何类A的实例或者继承自类A的类的实例,在序列化时都会返回null值,即不被序列化。
更复杂的序列化过滤规则,可以通过调整SpecificClassExclusionStrategy类中,两个重写方法来实现。