Android 进阶之第三方库的介绍 Gson
概述
做Android开发的每位工程师想必都会接触到Json,它是客户端与服务器进行数据交互时使用的一种数据交换格式。当然大家也可能只接触过XML,但是这种可能性不是很大,因为涉及到客户端和服务端交互这种事情,一般都不是自己能够说得算的,还要取决于服务端开发工程师的习惯,一般大家都比较推崇JSON格式,因为XML方式解析较为麻烦,并且XML形式的交换方式数据较大,不如JSON来得轻量。Java中最常用的JSON解析库有:JSON-Java、Gson、Jackson、FastJson等,这些类库就我个人来说并没都使用过,网上有其他人做过对比,这些解析库中来自阿里的FastJson性能最好。这个我自己没有亲自做过这方面的对比所以也不肯定,大家如果有兴趣找找对应的文章看看。
JSON语法简介
如果大家对JSON还不了解,可以到如下网站上了解下。它其实也是一种key/value的形式的数据交换方式.
http://www.json.org.cn/
http://www.w3school.com.cn/json/json_syntax.asp
下面是最核心的语法,大家如果不想花太多时间在这部分的话,可以简单下看下下面的语法,其实Json语法很简单花一两分钟就可以完全掌握了。
总体语法:
- 数据在名称/值对中
- 两个数据之间由逗号分隔
- 花括号保存对象
- 方括号保存数组
名值对定义:
JSON 名称/值对 定义如下"firstName" : "John"
变量名和数值使用冒号隔开。
JSON 值可以是:
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组(在方括号中)
- 对象(在花括号中)
- null
JSON 对象
JSON 对象在花括号中书写:
对象可以包含多个名称/值对:{ "firstName":"John" , "lastName":"Doe" }
JSON 数组
JSON 数组在方括号中书写:
数组可包含多个对象:{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}下面是一个比较简单的例子:
{
"firstName": "John",
"lastName": "Smith",
"sex": "female",
"age": 28,
"address":
{
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"
},
"phoneNumber":
[
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
往Android Studio中添加 GSON:
下面是GitHub的地址,上面给出了GSON的源码,文档地址等
https://github.com/google/gson
下面是一个推荐的GSON教程供进一步深入学习。
http://www.studytrails.com/java/json/java-google-json-introduction.jsp
要在Android studio中导入GSON,需要在gradle.build中添加如下依赖关系:
compile 'com.google.code.gson:gson:2.6.2' |
基本使用:
Gson中提供了fromJson() 和toJson() 两个直接用于解析和生成的方法,前者可以将数据从JSON中取出这个过程称为反序列化,后者用于将数据转化为JSON格式,称为序列化。
序列化
Gson gson = new Gson();
gson.toJson(100); //int
gson.toJson("jimmy"); //String
gson.toJson(new Long(1000000000)); //Long
gson.toJson(true); //boolean
gson.toJson(new Date()); //Date
int[] values = {1, 2, 3,4,5,6};
gson.toJson(values); //array反序列化
int one = gson.fromJson("100", int.class);
Integer oneInteger = gson.fromJson("100", Integer.class);
Long oneLong = gson.fromJson("1000", Long.class);
Boolean falseBoolean = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);POJO类的序列化和反序列化:
上面介绍的是最基本类的序列化和反序列化,但是上面的使用范围十分有限,更为常见的是POJO的序列化和反序列化:JavaBean 序列化
定义 Bean对象:class User {
private int id = 000001;
private String name = "jimmy";
private int age = 28;
public User() {
}
}序列化 JavaBean
Gson gson = new Gson();
String customjson = gson.toJson(new User());JavaBean 反序列化:
String jsonRes = "{\"id\":000001,\"name\":\"jimmy\",\"age\":25}"
User user = gson.fromJson(jsonRes, User.class);
GSON 流式序列化反序列化
- Gson的流式反序列化
String gsonString = "{\"name\":\"jimmy\",\"age\":\"28\"}";
User user = new User();
JsonReader gsonReader = new JsonReader(new StringReader(gsonString));
gsonReader.beginObject();
while (gsonReader.hasNext()) {
String tag = gsonReader.nextName();
switch (tag) {
case "name":
user.name = gsonReader.nextString();
break;
case "age":
user.age = gsonReader.nextInt();
break;
}
}
gsonReader.endObject(); - Gson的流式序列化
JsonWriter gsonwriter = new JsonWriter(new OutputStreamWriter(System.out));
gsonwriter.beginObject()
.name("name").value("jimmy")
.name("age").value(28)
.endObject();
gsonwriter.flush();
Gson中使用泛型
要在GSON中使用泛型必须使用TypeToken,当我们希望将数据解析为List
Gson gson = new Gson(); |
使用泛型
public class ResponeResult<T> { |
如何使用:
Type user = new TypeToken<ResponeResult<User>>(){}.getType(); |
默认GSON 的定制化:
一般情况下上面介绍的GSON已经够大家用了,但是还是有些常用的配置需要我们在序列化和反序列化的时候对其进行序列化和反序列化规则的配置:
Gson gson = new GsonBuilder() |
常用的注释标志
属性重命名 @SerializedName 注解的使用
从上面POJO的生成与解析可以看出在默认情况下GSON要求Bean的成员变量名和json字段名以及变量类型都应该一致,但是这个是很难保证的,一般客户端的开发和服务端的开发是分开来的,不可能服务端一修改我们这边就跟着改,特别是两个开发语言命名的规则不同的时候,一般都会存在纷争,GSON为了克服这个问题引入了@SerializedName。
|
这样GSON就会将来自服务端的json中的office_tel与Bean中的officeTel关联起来:
还有种比较难搞定的情况,Bean 中的某个字段对应对应服务器端的多个变量名的时候,可以使用如下方式为
为POJO字段提供备选属性名:
|
选择性导出
- @Expose
有的时候我们需要对POJO中的某些字段进行有选择性的导出,这时候我们就可以通过在要导出的字段上加上@Expose注解,不导出的字段不加的方式来达到这个目的:
@Expose(deserialize = true,serialize = true) //序列化和反序列化都生效 |
这种情况下就不能使用默认的GSON了,而必须使用上面介绍的GsonBuild进行定制:
Gson gson = new GsonBuilder() |
* @Since @Until基于版本号导出:
有时候我们的某些字段在某个版本是不需要导出的,这种情形就可以通过@Since 和 @Until,并结合GsonBuilder.setVersion(Double)来实现。这种情况个人是没有用到过,但是以后的开发中应该会有这种需求。
* 基于访问修饰符:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
.create();
* 自定义排除规则:
要自定义排除规则可以通过在创建GsonBuilder的时候通过addSerializationExclusionStrategy 和addDeserializationExclusionStrategy方法分别添加序列化和反序列化的排除规则。
Gson gson = new GsonBuilder() |
某种类型序列化和反序列化过程的自定义:
TypeAdapter
首先定义一个User类型的TypeAdapter,覆写里面的write以及read方法。这两个方法会在后续操作User.class的时候被调用。public class UserTypeAdapter extends TypeAdapter<User> {
@Override
public void write(JsonWriter out, User value) throws IOException {
out.beginObject();
out.name("name").value(value.name);
out.name("age").value(value.age);
out.endObject();
}
@Override
public User read(JsonReader in) throws IOException {
User user = new User();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "name":
user.name = in.nextString();
break;
case "age":
user.age = in.nextInt();
break;
}
}
in.endObject();
return user;
}
}实现TypeAdapter后还需要通过registerTypeAdapter注册给GsonBuilder,在注册的时候需要指定要关联的对象以及对应的TypeAdapter类型。
User user = new User("jimmy", 28);
Gson gson = new GsonBuilder()
//为User注册TypeAdapter
.registerTypeAdapter(User.class, new UserTypeAdapter())
.create();再考虑如下情况,如果有多个类继承自同一个父类,并且我们对这一系列的子类都使用同一种方式序列化以及反序列化,那么这种情况有两种做法,一种是使用registerTypeAdapter对一个个子类进行注册,另一种是使用registerTypeHierarchyAdapter注册父类即可。但是一定要注意registerTypeHierarchyAdapter不支持泛型。
JsonSerializer与JsonDeserializer
上面的registerTypeAdapter必须同步指定序列化和反序列化的实现,如果并不是想序列化和反序列化两个过程都重新覆写,只是想实现其中之一那么可以使用JsonSerializer与JsonDeserializer来代替TypeAdapter
下面是来自http://www.studytrails.com/java/json/java-google-json-custom-serializer-deserializer.jsp 的JsonDeserializer用法的例子public class DogDeserialiser implements JsonDeserializer<Dog> {
@Override
public Dog deserialihttp://www.studytrails.com/java/json/java-google-json-custom-serializer-deserializer.jspze(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String name = json.getAsJsonObject().get("name").getAsString();
name = name.replace(" ", "_");
Dog dog = new Dog(name);
return dog;
}
public static void main(String[] args) {
String json = "{\"animal\":{\"name\":\"I am a dog\"}}";
Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class,
new DogDeserialiser()).create();
Type animalType = new TypeToken<Animal<Dog>>() {
}.getType();
Animal<Dog> animal = gson.fromJson(json, animalType);
System.out.println(animal.get().getName());
}
}JsonSerializer
public class DogSerializer implements JsonSerializer<dog> {
@Override
public JsonElement serialize(Dog src, Type typeOfSrc,JsonSerializationContext context) {
// This method gets involved whenever the parser encounters the Dog
// object (for which this serializer is registered)
JsonObject object = new JsonObject();
String name = src.getName().replaceAll(" ", "_");
object.addProperty("name", name);
// we create the json object for the dog and send it back to the
// Gson serializer
return object;
}
public static void main(String[] args) {
Animall<Dog> animal = new Animall<Dog>();
Dog dog = new Dog("I am a dog");
animal.setAnimal(dog);
// Create the GsonBuilder and register a serializer for the Dog class.
// Whenever the Dog class is encountered Gson calls the DogSerializer
// we set pretty printing own to format the json
Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class, new DogSerializer()).setPrettyPrinting().create();
// Since Animal contains generic type create the type using TypeToken
// class.
Type animalType = new TypeToken<Animal<Dog>>() {
}.getType();
System.out.println(gson.toJson(animal, animalType));
}
}需要注意的是registerTypeAdapter必须使用包装类型不能使用int.class,long.class,float.class
如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。Type type = new TypeToken<List<User>>() {}.getType();
TypeAdapter typeAdapter = new TypeAdapter<List<User>>() {
};
Gson gson = new GsonBuilder()
.registerTypeAdapter(type, typeAdapter)
.create();
List<User> list = new ArrayList<>();
list.add(new User("jimmy",28));
list.add(new User("kitty",22));
String result = gson.toJson(list, type);TypeAdapterFactory
除了用上述的方式注册TypeAdapter外还可以使用TypeAdapterFactory,可以根据传入的类型来查看目前是否有现有的TypeAdapter,如果有的话就返回对应的TypeAdapter并注册,如果没有就返回null。注册工厂的方法为GsonBuilder.registerTypeAdapterFactory。@JsonAdapter
使用@JsonAdapter注解可以替代registerTypeAdapterFactory以及registerTypeAdapter方法,这样可以避免在每次进行序列化和反序列化的时候都要进行注册。它的参数必须是且必须是TypeAdpater,JsonSerializer/JsonDeserializer或者TypeAdapterFactory。
用法如下:@JsonAdapter(DogTypeAdapter.class)
public class Dog {
}