前言
手机应用现在都已经实现联网功能,在移动网络高效的传输数据是每个应用必备的功能。目前绝大多数的服务器交换数据类型都使用Json格式,它相对于XML等其他格式占用的带宽更少,而且支持各种嵌套实现复杂的数据格式。现在来使用Google提供的Gson框架实现Json数据的解析操作。
对象转Json
使用Gson只需要简单的创建一个Gson对象,将普通的对象转换成Json格式的字符串只需要调用Gson对象的toJson方法。
首先定义一个简单的JavaBean对象User,下面只有简单的属性定义,省略了那些读取和设置方法。
public class User {
private String id;
private String name;
private int age;
private int isAdult = -1;
public User() {
}
public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
...
// 各种getter/setter
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", isAdult=" + isAdult +
'}';
}
}
使用Gson对象将User对象转换成json字符串。
Gson gson = new Gson();
User user = new User("1000", "zhangshan", 20);
String jsonStr = gson.toJson(user);
// 结果
{"id":"1000","name":"zhangshan","userAge":20,"isAdult":-1}
结果很简单就是一个Json对象,里面包含了所有定义函数的值。
在来把List类型的User转换成一个Json字符串。
Gson gson = new Gson();
User user = new User("1000", "zhangshan", 20);
User user2 = new User("2000", "lisi", 30);
User user3 = new User("3000", "wangwu", 25);
List<User> list = new ArrayList<>();
list.add(user);
list.add(user2);
list.add(user3);
String jsonStr = gson.toJson(list);
System.out.println(jsonStr);
// 结果
[{"id":"1000","name":"zhangshan","userAge":20,"isAdult":-1},
{"id":"2000","name":"lisi","userAge":30,"isAdult":-1},
{"id":"3000","name":"wangwu","userAge":25,"isAdult":-1}]
结果也很直观就是一个数组,里面包含三个User对象。
将Json转换成对象
服务器端返回的数据都是Json格式的,但是Android使用Java这种面向对象的编程语言,将Json转换成对象更加符合开发者的开发习惯。简单的单个对象数据转换如下。
private String userJson = "{\"id\":\"1000\",\"name\":\"zhangshan\",\"age\":20}";
User user = gson.fromJson(userJson, User.class);
System.out.println(user);
// 结果
User{id='1000', name='zhangshan', age=0, isAdult=-1}
打印的结果就是生成的User对象toString返回的字符串,可以看出转换很成功。现在来看看如何实现将对象数组转换成为对象列表,因为List使用了泛型类型,而泛型在编译时会被擦出,Gson中提供了一个TypeToken的工具类,使用这个工具类生成一个匿名子类就可以获取泛型类的实际类型。
private String userListJson = "[{\"id\":\"1000\",\"name\":\"zhangshan\",\"age\":20},{\"id\":\"2000\",\"name\":\"lisi\",\"age\":30},{\"id\":\"3000\",\"name\":\"wangwu\",\"age\":25}]";
List<User> userList = gson.fromJson(userListJson, new TypeToken<List<User>>(){}.getType());
System.out.println(userList);
// 结果
[User{id='1000', name='zhangshan', age=0, isAdult=-1},
User{id='2000', name='lisi', age=0, isAdult=-1},
User{id='3000', name='wangwu', age=0, isAdult=-1}]
结果打印除了List的toString方法返回的字符串,可以看出成功的将List对象解析出来了。
Gson Annotation使用
Gson的注解主要包含@SerializedName,为属性取别名。开发Android项目中通常使用proguard混淆工具将里面的源代码全部变成各种a,b,c等字符来提高反编译项目源代码的难度,这时候对象的属性名就被改变了,如果再从Json字符串解析就无法对应正确的属性赋值,@SerializedName注解就能很好的解决这个问题。@Export注解用于在生成Gson对象时配置了excludeFieldsWithoutExposeAnnotation时需要序列化的属性,如果属性没有配置这个注解就不会被序列化。
现在为User添加上两个注解,同时新添加一个Group对象,为它也添加上注解,实现代码如下:
public class User {
@Expose
private String id;
private String name;
@Expose
@SerializedName("userAge")
private int age;
private int isAdult = -1;
public User() {
}
public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// getter/setter
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", isAdult=" + isAdult +
'}';
}
}
可以看到User里的name没有添加@Expose注解,而age添加了@SerializedName(“userAge”)注解,再来定义一个Group类,它的实现如下:
public class Group {
@Expose
private String id;
@Expose
private String name;
@Expose
private Set<User> userList;
private int count;
// getter/setter
@Override
public String toString() {
return "Group{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", userList=" + userList +
", count=" + count +
'}';
}
}
可以看到Group的count未加expose注解,那么最终生成的json数据如下:
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Group group = new Group();
group.setId("200000");
group.setName("Android开发群");
User user = new User("1000", "zhangshan", 20);
User user2 = new User("2000", "lisi", 10);
User user3 = new User("3000", "wangwu", 15);
Set<User> set = new HashSet<>();
set.add(user);
set.add(user2);
set.add(user3);
group.setUserList(set);
group.setCount(set.size());
String json = gson.toJson(group);
System.out.println(json);
// 结果
{"id":"200000","name":"Android开发群",
"userList":[{"id":"1000","userAge":20},
{"id":"3000","userAge":15},
{"id":"2000","userAge":10}]}
可以看到Group.count和User.name都未序列化,而age则变成了userAge输出。
自定义解析和反解析
前面展示的例子都是非常简单的数据对象,实际开发中见到的对象都是比较复杂的,对这些复杂对象有时侯需要自己手动来解析它们。Json字符串解析成对象其实是反序列化的过程,对象转换成Json字符串则是序列化的过程,如果两个过程都需要手动做,那么可以使用TypeAdapter转换器来实现。现在来实现手动的解析和反解析User对象。
public class UserTypeAdapter extends TypeAdapter<User> {
@Override
public void write(JsonWriter out, User value) throws IOException {
// 开始对象
out.beginObject();
// 写入对象属性
out.name("id").value(value.getId());
out.name("name").value(value.getName());
out.name("age").value(value.getAge());
out.endObject();
}
@Override
public User read(JsonReader in) throws IOException {
// 解析json字符串
User user = new User();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "id":
user.setId(in.nextString());
break;
case "name":
user.setName(in.nextString());
break;
case "age":
int age = in.nextInt();
// 如果年龄大于18认为是成年人
user.setAge(age);
user.setAdult(age == 18 ? 0 : age > 18 ? 1 : 2);
break;
}
}
in.endObject();
return user;
}
}
上面的UserTypeAdapter能在解析Json数据的时候做其他的设置操作,这在项目开发中是一个很有用的技巧。
这个转换器需要在Gson生成对象的时候注册进去,这样每次遇到User类的转换都会调用用户自定义的转换方法。
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.registerTypeAdapter(User.class, new UserTypeAdapter())
.create();
User user = gson.fromJson(userJson, User.class);
System.out.println(user);
// 结果
User{id='1000', name='zhangshan', age=20, isAdult=1}
可以看到isAdult属性被设置了。
如果用户只希望在一个方向比如序列化自定义完成,另外一个方向反序列化自动完成,或者反过来,使用JsonSerializer和JsonDeserializer两者就可以了。
public class UserSerializer implements JsonSerializer<User> {
@Override
public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.addProperty("id", src.getId());
return object;
}
}
public class UserDeserializer implements JsonDeserializer<User> {
@Override
public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
User user = new User();
JsonObject object = json.getAsJsonObject();
// 将id设置到name属性上
user.setName(object.get("id").getAsString());
return user;
}
}
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.registerTypeAdapter(User.class, new UserDeserializer())
.registerTypeAdapter(User.class, new UserSerializer())
.create();
User user = gson.fromJson(userJson, User.class);
System.out.println(user);
// 结果
User{id='null', name='1000', age=0 }
可以看到成功把id设置到了name属性上,并且只有id这个属性被序列化了。
总结
Gson框架为开发这提供了功能强大的Json解析功能,除了上面的基础使用方法还有JsonReader和JsonWriter这些高级的使用方式,相对来说比较少用,也更加复杂,大部分的功能使用以上的技巧都能满足,这里就不再介绍高级的用法。