概述
序列化:将数据结构或对象转换成可用于存储或者传输的数据格式的过程;
反序列化:将序列化过程中生成的数据还原成数据结构或者对象的过程;
对于数据的序列化与反序列化,Gson框架一直是我的首选。自己封装了一套Retrofit+OkHttp+RxAndroid的简易框架,其中的数据解析一直用的是Gson。由于Retrofit框架内部已经完成了对Gson序列化与反序列化的操作封装,一直少有接触Gson真正的序列化与反序列化操作,至于阅读源码什么的,就更不可能了。趁着最近有点空余的时间,赶紧去Gson官网看了看,写点笔记,补补这部分的空缺。(原谅我对于Gson的操作一直是停留了toGson(),fromGson()这两个Api的)。写完这个系列的博客,我希望我对Gson认知能够达到的水准为
- Gson的基本使用
- Gson的进阶自定义序列化与反序列化
- Gson的源码分析序列化与反序列化过程
- 解决自己在开发中遇到数据解析难题
基本使用
1.Android Studio 导包
dependencies {
compile 'com.google.code.gson:gson:2.8.5'
}
2.基本类型数据
基本数据类型的序列化与反序列化操作都比价简单
Gson gson = new Gson();
gson.toJson(new Character('a')); // ==>"a"
gson.toJson(1); // ==>1
gson.toJson(1.0f); // ==>1.0
gson.toJson(1.0d); // ==>1.0
gson.toJson(1l); // ==>1
gson.toJson(true); // ==>true
gson.toJson("hello world"); // ==>"hello world"
gson.fromJson("\'a\'", char.class); // ==> a
gson.fromJson("1", int.class); // ==>1
gson.fromJson("1.0", float.class); // ==>1.0
gson.fromJson("1.0", double.class); // ==>1.0
gson.fromJson("1", long.class); // ==>1
gson.fromJson("true", boolean.class); // ==>true
gson.fromJson("\"hello world\"", String.class); // ==>hello world
对于那些具备自动封箱操作的基本数据类型只要他们反序列化的数据源一样,则生成的对象也是一样的
Integer one = gson.fromJson("1", int.class);
Integer two = gson.fromJson("1", Integer.class);
Log.d(TAG,"=="+(one==two)); // ==> true;
Log.d(TAG,"equals"+(one.equals(two))); // ==>true;
3.数组
对于数组的序列化与反序列化,针对自动装箱的基本数据类型数据的数组,需要特别注意的是他们序列化的结果可能结果,但是反序列化是不同的。(如果把数组作为对象来看待则可以很简单的理解);
int[] intArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
Integer[] integerArray = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
String one = gson.toJson(intArray); // ==>[1,2,3,4,5,6,7,8,9]
String two = gson.toJson(integerArray); // ==>[1,2,3,4,5,6,7,8,9]
int[] ints = gson.fromJson("[1,2,3,4,5,6,7,8,9]", int[].class);
Integer[] integers = gson.fromJson("[1,2,3,4,5,6,7,8,9]", Integer[].class);
Log.d(TAG, "equals" + (ints.equals(integers))); // ==>false;
4.容器
java的容器应为涉及到泛型,所以在反序列化的时候,需要声明泛型的类型,通过TypeToken这个类可以完美的解决;
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
//反序列化
gson.toJson(list); // ==> [1,2,3,4,5]
//序列化
List oneList = gson.fromJson("[1,2,3,4,5]", List.class); // 错误的==> [1.0,2.0,3.0,4.0,5.0]
Type type = new TypeToken<List<Integer>>() {}.getType();
List twoList = gson.fromJson("[1,2,3,4,5]", type); // 正确的==> [1,2,3,4,5]
5.泛型数据
在容器中已经涉及到了泛型数据,我们需要用TypeToken这个类可以构建我们在泛型中使用的具体类型,从而在反序列化的时候,告诉Gson,我们需要组织的数据类型。当类设计到泛型的时候,依然是这个操作,下面试具体的操作:
No1:自定义一个类,在没有涉及到泛型的时候,序列化和反序列化是这样的:
public class GsonBean {
String name;
int age;
boolean falg;
public GsonBean(String name, int age, boolean falg) {
this.name = name;
this.age = age;
this.falg = falg;
}
}
GsonBean javaBean = new GsonBean("Java", 55, true);
// 序列化;
gson.toJson(javaBean); // ==> {"age":55,"falg":true,"name":"Java"}
// 反序列化;
GsonBean gsonBean = gson.fromJson("{\"age\":55,\"falg\":true,\"name\":\"Java\"}",
GsonBean.class); // ==> {"age":55,"falg":true,"name":"Java"}
No2:如果GsonBean是一个泛型类
public class GsonBean<T> {
String name;
int age;
boolean falg;
T obj;
public GsonBean(String name, int age, boolean falg,T t ) {
this.name = name;
this.age = age;
this.falg = falg;
this.obj = t;
}
}
GsonBean<String> gsonBean = new GsonBean<>();
gsonBean.setName("PHP");
gsonBean.setAge(22);
gsonBean.setFalg(false);
gsonBean.setObj("Welcome to PHP");
// 序列化;
gson.toJson(gsonBean); // ==> {"age":22,"falg":false,"name":"PHP","obj":"Welcome to PHP"}
// 下面这种反序列化的方式有可能会返回错误的结果;
GsonBean errorBean = gson.fromJson("{\"age\":22,\"falg\":false,\"name\":\"PHP\"," +
"\"obj\":\"Welcome to PHP\"}", GsonBean.class);
// ==> {"age":22,"falg":false,"name":"PHP","obj":"Welcome to PHP"}
// 这种反序列化的方式才是正确的
Type type = new TypeToken<GsonBean<String>>() {}.getType();
GsonBean rightBean = gson.fromJson("{\"age\":22,\"falg\":false,\"name\":\"PHP\"," +
"\"obj\":\"Welcome to PHP\"}", type);
// ==> {"age":22,"falg":false,"name":"PHP","obj":"Welcome to PHP"}
6.内部类
内部类的的序列化与反序列化是一个比较细节的问题。不过综合起来就是对一句话的理解,“非静态内部类持有外部类的引用”,感觉听这句话听到耳朵起茧的感觉,其实这句话在编程中非常有用,因为这句话很多人可能深陷内存泄漏的沼泽无法自拔,而在Gson序列化与反序列化的过程中,这句话也发挥着非常重要的作用。先举一个小栗子,通过GsonFormate插件让一个json字符串生成对应的bean类;
字符串为
{
"type":"game",
"item":{
"name":"lol",
"age":3
}
}
GsonFormat 插件自动生成的Bean对象为
public class TypeBean {
/**
* type : game
* item : {"name":"lol","age":3}
*/
private String type;
private ItemBean item;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public ItemBean getItem() {
return item;
}
public void setItem(ItemBean item) {
this.item = item;
}
public static class ItemBean {
/**
* name : lol
* age : 3
*/
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
从上面的代码发现TypeBean这个类中拥有一个内部类ItemBean,对于这个类的具体类型,很少人会去注意。这个类的修饰符是static,是一个静态内部类。为什么要用静态内部类?这个问题你不需要知道。但是我们必须记住这点,它是一个静态内部类。那如果让这个内部类变为非静态内部类会怎么样,试一试。
TypeBean typeBean = new TypeBean();
typeBean.setType("饮食");
TypeBean.ItemBean itemBean = new TypeBean.ItemBean();
itemBean.setName("舌尖上的中国");
itemBean.setAge(4);
typeBean.setItem(itemBean);
gson.toJson(typeBean); // ==> {"item":{"age":4,"name":"舌尖上的中国"},"type":"饮食"}
Log.d(TAG,gson.toJson(typeBean));
// 反序列化
TypeBean typeBean1 = gson.fromJson("{\"item\":{\"age\":4,\"name\":\"舌尖上的中国\"}," +
"\"type\":\"饮食\"}", TypeBean.class);
// 可以反序列化非静态内部类;
TypeBean.ItemBean item = gson.fromJson("{\"age\":4,\"name\":\"舌尖上的中国\"}", TypeBean
.ItemBean.class);
// static ==> {"age":4,"name":"舌尖上的中国"}
// 非static ==> {"age":4,"name":"舌尖上的中国"}
Log.d(TAG,gson.toJson(item));
代码上貌似没有什么区别,发现好像不会发生什么Crash什么的。但是,理论上而言,如果使用了非静态内部类,那么这个内部类持有外部类引用。我们单纯用内部类对应的gson字符串来反序列化一个内部类对象,这种做法显然是不合理的。因为这样构造出的对象显然缺失了外部类的引用。至于为什么代码中会没有体现,猜想可能是异常被拦截,或者源码做了修改,后面会通过源码查看原因。
7.多类型容器数据
涉及到多类型的容器的的序列化与反序列化的时候,他的序列化过程是需要特别的其他操作的。但是反序列化就需要我们自己通过Gson解析器去实现
List multi = new ArrayList<>();
multi.add(1);
multi.add("hello");
multi.add(new Person("XiaoMi",22));
// 序列化是没有问题的
gson.toJson(multi); // ==>[1,"hello",{"age":22,"name":"XiaoMi"}]
// 反序列化则需要通过解析器来操作完成;
//NO1 实例化一个解析器
JsonParser jsonParser = new JsonParser();
// 因为是list,所以解析成数组;
JsonArray jsonArray = jsonParser.parse(gson.toJson(multi)).getAsJsonArray();
Integer aInt = gson.fromJson(jsonArray.get(0), int.class);
String aString = gson.fromJson(jsonArray.get(1), String.class);
Person aPerson = gson.fromJson(jsonArray.get(2), Person.class);
Log.d(TAG,aInt+" : "+aString+" : "+aPerson.toString()); // ==> [1,"hello",{"age":22,"name":"XiaoMi"}]
Log.d(TAG,gson.toJson(multi));
Ok,Gson基本的常规操作差不多就这样了,下面就需要去自定义序列化与自定义反序列化了。