JSON解析类库之Gson(4) --- TypeAdapter接管序列化与反序列化
来源:互联网 发布:常用协议端口号 编辑:程序博客网 时间:2024/06/05 23:48
JSON解析类库之Gson(4) --- TypeAdapter接管序列化与反序列化
---Gson类库学习, 生成与解析json数据,json字符串与Java对象互转
一、前言
本文介绍高级的Gson用法。讲解Gson的最新技术,流式API处理序列化与反序列化技术TypeAdapter。
二、TypeAdapter高效流式处理
◆◆◆ 1 TypeAdapter (Gson 2.1之后的新方法,推荐)
----------------------------------------------------------------------------------------------------
TypeAdapter 是Gson自2.0(源码注释上说的是2.1)开始版本提供的一个抽象类,用于接管某种类型的序列化和反序列化过程,包含两个主要方法 write(JsonWriter,T) 和 read(JsonReader) 其它的方法都是final方法并最终调用这两个抽象方法。
注:如果没有显式编写和注册TypeAdapter,用了诸如@SerializedName()注解或其他的GsonBuilder方法,系统也会根据TypeToken类型,生成一个默认的TypeAdapter<T>。所以最新版本的Gson是非常快的,跟Fastjson半斤八两差不多,但Gson的代码质量高,稳定无bug。(Fastjson最近还爆出个一个漏洞,现在已经修复。但Gson从2008发布到现在还没出现或这样情况,一直都很稳定,2.0引入流式处理后效率一直很高、很稳定)。不管是Java开发,还是android开发,Gson解析库都是最佳选择(个人观点,勿喷)。
看看TypeAdapter的源码:
public abstract class TypeAdapter<T> {/*** Writes one JSON value (an array, object, string, number, boolean or null)* for {@code value}.** @param value the Java object to write. May be null.*/public abstract void write(JsonWriter out, T value) throws IOException;/*** Reads one JSON value (an array, object, string, number, boolean or null)* and converts it to a Java object. Returns the converted object.** @return the converted Java object. May be null.*/public abstract T read(JsonReader in) throws IOException;// 省略了其他方法,请查看源码...}
注意:TypeAdapter 以及 JsonSerializer 和 JsonDeserializer 都需要与 GsonBuilder.registerTypeAdapter 或GsonBuilder.registerTypeHierarchyAdapter 配合使用,进行注册,下面将不再重复说明。
◇情况1:TypeAdapter接管JavaBean的序列化与反序列化
实体类:
public class User {private String id;private String name;private Date birthday;//为了代码简洁,这里移除了getter和setter方法、toString方法、构造方法等}
测试类:
package com.chunlynn.gson;import java.util.Date;import com.google.gson.Gson;import com.google.gson.GsonBuilder;public class GsonTest6 {public static void main(String[] args) {User user = new User("1", "王重阳", new Date());Gson gson = new GsonBuilder().setPrettyPrinting()//格式化输出,这个配置有效//User对象的序列化和序列化全都交给UserTypeAdapter了,其他配置就无效了.registerTypeAdapter(User.class, new UserTypeAdapter()).create();/*** TypeAdapter流式序列化*/String jsonString = gson.toJson(user);System.out.println("使用UserTypeAdapter:\n" + jsonString);/** 使用UserTypeAdapter:{"id": "1","name": "王重阳","birth": "2017-05-02 22:39:19"}*//*** 使用UserTypeAdapter流式反序列化*/user = gson.fromJson(jsonString, User.class);System.out.println("流式反序列化:\n" + user);// 流式反序列化: User [id=1, name=王重阳, birthday=Tue May 02 23:47:12 CST 2017]}}
UserTypeAdapter类:
package com.chunlynn.gson;import java.io.IOException;import java.text.ParseException;import java.text.SimpleDateFormat;import com.google.gson.TypeAdapter;import com.google.gson.stream.JsonReader;import com.google.gson.stream.JsonWriter;/*** UserTypeAdapter用来接管User对象的序列化与反序列化** @ClassName UserTypeAdapter* @author chunlynn* @date 2017年5月2日* @Version V1.0*/public class UserTypeAdapter extends TypeAdapter<User> {/*** 流式序列化(速度快),将序列化后的值写到流对象中*/@Overridepublic void write(JsonWriter out, User value) throws IOException {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");out.beginObject(); //流式序列化成对象开始out.name("id").value(value.getId());out.name("name").value(value.getName());out.name("birth").value(dateFormat.format(value.getBirthday()));//格式化日期输出//out.name("birth").value(String.valueOf(value.getBirthday()));//这样就没有格式化了out.endObject(); //流式序列化结束}/*** 反序列化,从json流对象中读取*/@Overridepublic User read(JsonReader in) throws IOException {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");User user = new User();in.beginObject(); //流式反序列化开始while (in.hasNext()) {//此处遍历官方推荐用if-else比较好,因为if-else可以属性值判断处理switch (in.nextName()) {case "id":user.setId(in.nextString());break;case "name":user.setName(in.nextString());break;case "birthday":case "birth_day":case "birth":try {user.setBirthday(dateFormat.parse(in.nextString()));} catch (ParseException e) {e.printStackTrace();}break;}}in.endObject(); //流式反序列化结束return user;}}
当我们为User.class注册了TypeAdapter之后,只要是操作User.class ,那些之前介绍的@SerializedName 、FieldNamingStrategy、@Since、@Until、@Expose通通都黯然失色,失去了效果,只会调用我们实现的UserTypeAdapter.write(JsonWriter, User) 方法,我想怎么写就怎么写。
◇情况2:TypeAdapter接管泛型的序列化与反序列化
测试类:
package com.chunlynn.gson;import java.io.IOException;import java.lang.reflect.Type;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.List;import com.google.gson.Gson;import com.google.gson.GsonBuilder;import com.google.gson.TypeAdapter;import com.google.gson.reflect.TypeToken;import com.google.gson.stream.JsonReader;import com.google.gson.stream.JsonWriter;public class GsonTest21 {public static void main(String[] args) {/*** TypeAdapter可以这里定义(但无法重用了),所以建议提取出来如UserListTypeAdapter.java*/TypeAdapter<List<User>> userListTypeAdapter = new TypeAdapter<List<User>>() {// 序列化[1]@Overridepublic void write(JsonWriter out, List<User> value) throws IOException {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");out.beginArray(); //流式序列化结束 [for (User user : value) { //User的序列化也可以提取出来out.beginObject(); //流式序列化结束{out.name("id").value(user.getId());out.name("name").value(user.getName());out.name("birth").value(dateFormat.format(user.getBirthday()));out.endObject(); //流式序列化结束 }}out.endArray();// 流式序列化结束 [}// 反序列化[2]@Overridepublic List<User> read(JsonReader in) throws IOException {List<User> listUsers = new ArrayList<User>();in.beginArray(); // 开始解析流中数组中的对象while (in.hasNext()) {try {listUsers.add(readUser(in)); // 流式对象解析} catch (ParseException e) {e.printStackTrace();}}in.endArray(); // 数组解析流关闭return listUsers;}public User readUser(JsonReader in) throws IOException, ParseException {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String userId = null;String userName = null;Date userBirthdayDate = null;in.beginObject();while (in.hasNext()) {String propertyName = in.nextName();if (propertyName.equals("id")) {userId = in.nextString();} else if (propertyName.equals("name")) {userName = in.nextString();} else if (propertyName.equals("birthday") || propertyName.equals("birth")) {userBirthdayDate = dateFormat.parse(in.nextString());}}in.endObject();return new User(userId, userName, userBirthdayDate);}};User user1 = new User("1", "王重阳", new Date());User user2 = new User("2", "郭靖", new Date());User user3 = new User("3", "黄蓉", new Date());List<User> userList = new ArrayList<User>();userList.add(user1);userList.add(user2);userList.add(user3);// 泛型的类型,序列化和反序列时都要带上该参数Type type = new TypeToken<List<User>>() {}.getType();Gson gson = new GsonBuilder()////.registerTypeAdapter(type, new UserListTypeAdapter()).create();//TypeAdapter提取出来.registerTypeAdapter(type, userListTypeAdapter).create();String jsonString = gson.toJson(userList, type);System.out.println("泛型的序列化,使用TypeAdapter==》" + jsonString);/* 泛型的序列化,使用TypeAdapter==》[{"id":"1","name":"王重阳","birth":"2017-05-06 21:16:04"},{"id":"2","name":"郭靖","birth":"2017-05-06 21:16:04"},{"id":"3","name":"黄蓉","birth":"2017-05-06 21:16:04"}] */List<User> retListUsers = gson.fromJson(jsonString, type);for (User u : retListUsers) {System.out.println("泛型的反序列化,使用TypeAdapter==》" + u);}//泛型的反序列化,使用TypeAdapter==》User [id=1, name=王重阳, birthday=Sat May 06 21:16:04 CST 2017]//泛型的反序列化,使用TypeAdapter==》User [id=2, name=郭靖, birthday=Sat May 06 21:16:04 CST 2017]//泛型的反序列化,使用TypeAdapter==》User [id=3, name=黄蓉, birthday=Sat May 06 21:16:04 CST 2017]}}
如果TypeAdapter实例化提取出来如下:
package com.chunlynn.gson;import java.io.IOException;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.List;import com.google.gson.TypeAdapter;import com.google.gson.stream.JsonReader;import com.google.gson.stream.JsonWriter;/*** UserListTypeAdapter用来接管List<User>泛型对象的序列化与反序列化* <p> 流式处理,高效率,任何类型都可以处理** @ClassName UserListTypeAdapter* @author chunlynn* @date 2017年5月6日* @Version V1.0*/public class UserListTypeAdapter extends TypeAdapter<List<User>> {/*** 流式序列化(速度快),将序列化后的值写到流中,需要注意开启流、关闭流*/@Overridepublic void write(JsonWriter out, List<User> value) throws IOException {out.beginArray(); //1 流式数组序列化开始 "]"for (User user : value) {writeUser(out, user); //2 流式对象序列化,"{ }"}out.endArray(); //1 流式数组序列化结束 "]"}private void writeUser(JsonWriter out, User user) throws IOException {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");out.beginObject(); //流式对象序列化开始,生成"{"out.name("id").value(user.getId());out.name("name").value(user.getName());out.name("birth").value(dateFormat.format(user.getBirthday()));out.endObject(); //流式对象序列化结束,生成"}" }/*** 反序列化,从JsonReader流中读取* @throws IOException*/@Overridepublic List<User> read(JsonReader in) throws IOException {List<User> listUsers = new ArrayList<User>();in.beginArray(); // "["while (in.hasNext()) {try {listUsers.add(readUser(in)); // "{ }"} catch (ParseException e) {e.printStackTrace();}}in.endArray(); //"]"return listUsers;}public User readUser(JsonReader in) throws IOException, ParseException {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String userId = null;String userName = null;Date userBirthdayDate = null;in.beginObject();while (in.hasNext()) {String propertyName = in.nextName();if (propertyName.equals("id")) {userId = in.nextString(); //这里也可以通过setter方法赋值 } else if (propertyName.equals("name")) {userName = in.nextString();} else if (propertyName.equals("birthday") || propertyName.equals("birth")) {userBirthdayDate = dateFormat.parse(in.nextString());}}in.endObject();return new User(userId, userName, userBirthdayDate);}}
特别注意:in.beginObject();和 in.endObject();一定要配对,并且要执行,否则会报错。其他的同理。
◆◆ TypeAdapter中的write方法和read方法使用说明
-----------------------------------------------------------------------
说明:
下面对两个write方法和read方法进行分别的阐述:
1 TypeAdapter中的write方法
write()方法中会传入JsonWriter,和需要被序列化的List<User>对象的实例,采用和PrintStream类似的方式写入到JsonWriter中。
下面是上面代码的步骤:
out.beginArray产生 "[ ",表示我们希望产生的是一个数组对象。使用beginObject()产生"{",产生一个对象。同理,out.endArray会产生" ]",out.endObject会产 " } " 。
out.name("id").value(user.getId());out.name("name").value(user.getName());分别获取User中的id和name字段,并且设置给Json对象中的id和name。也就是说上面这段代码,会在json对象中产生:
[{"id":"1","name":"王重阳","birth":"2017-05-06 21:16:04"},
{"id":"2","name":"郭靖","birth":"2017-05-06 21:16:04"},
{"id":"3","name":"黄蓉","birth":"2017-05-06 21:16:04"}]
{"id":"2","name":"郭靖","birth":"2017-05-06 21:16:04"},
{"id":"3","name":"黄蓉","birth":"2017-05-06 21:16:04"}]
这里需要注意的是,如果没有调用 out.endObject()产生}, 或out.endArray产生 ] ,那么你的项目会报出错误和异常。
2 TypeAdapter中的read方法
read()方法将会传入一个JsonReader对象实例,并返回反序列化的对象。
下面是这段代码的步骤:
同样是通过in.beginObject();和in.endObject();对应解析"{","}"
通过如下方式:
while (in.hasNext()) {String propertyName = in.nextName();if (propertyName.equals("id")) {userId = in.nextString(); //这里也可以通过setter方法对JavaBean属性赋值} else if (propertyName.equals("name")) {userName = in.nextString();} else if (propertyName.equals("birthday") || propertyName.equals("birth")) {userBirthdayDate = dateFormat.parse(in.nextString());}}
来完成每个token的遍历,并且通过if-else的方法获取Json对象中的键值对。并通过我们User实体类的Setter方法进行设置。也可以通过构造方法进行设置。
◆◆◆ 2 JsonSerializer与JsonDeserializer (早期Gson 1.x的方法,可以分开序列化和反序列化用,性能不怎么好,已不推荐)
----------------------------------------------------------------------------------------------------
JsonSerializer 和JsonDeserializer 不用像TypeAdapter一样,必须要实现序列化和反序列化的过程,你可以根据需要选择,如只接管序列化的过程就用 JsonSerializer ,只接管反序列化的过程就用 JsonDeserializer 。
由于JsonSerializer 和JsonDeserializer 使用了一个中间对象JsonElement来进行序列化和反序列化,所以效率、速度都不怎么样,内存占用也大,所以基本淘汰。而TypeAdapter省略了这个中间对象,直接写入到流中,所以效率极大提高。在下一篇文章中将专门将讲一讲TypeAdapter和JsonSerializer 、JsonDeserializer 的效率与区别。
所以不打算细讲,因为已经太旧了。
简单举例:
对Integer类型的反序列化:
注意:同TypeAdapter一样,也需要在GsonBuilder中注册配置。
Gson gson = new GsonBuilder().setPrettyPrinting()//格式化输出.registerTypeAdapter(Integer.class, new JsonDeserializer<Integer>() {@Overridepublic Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)throws JsonParseException {try {return json.getAsInt(); //将JSON字符串转为Integer类型} catch (NumberFormatException e) {return -1; //空字符串转为 -1}}}).create();System.out.println(gson.toJson(100)); //结果:100System.out.println(gson.fromJson("\"\"", Integer.class)); //结果-1
下面是所有数字都转成序列化为字符串的例子
package com.chunlynn.gson;import java.lang.reflect.Type;import com.google.gson.Gson;import com.google.gson.GsonBuilder;import com.google.gson.JsonElement;import com.google.gson.JsonPrimitive;import com.google.gson.JsonSerializationContext;import com.google.gson.JsonSerializer;public class GsonTest19 {public static void main(String[] args) {// 数字类型序列化器JsonSerializer<Number> numberJsonSerializer = new JsonSerializer<Number>() {@Overridepublic JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {return new JsonPrimitive(String.valueOf(src));}};/** 可以在GsonBuilder中注册多个TypeAdapter*/Gson gson = new GsonBuilder().registerTypeAdapter(Integer.class, numberJsonSerializer).registerTypeAdapter(Long.class, numberJsonSerializer).registerTypeAdapter(Float.class, numberJsonSerializer).registerTypeAdapter(Double.class, numberJsonSerializer).create();// 浮点型转成字符串System.out.println(gson.toJson(100.0f));//结果:"100.0"}}
注:registerTypeAdapter必须使用包装类型,所以int.class,long.class,float.class和double.class是行不通的。同时不能使用父类来替上面的子类型,这也是为什么要分别注册而不直接使用Number.class的原因。
上面特别说明了registerTypeAdapter不行,那就是有其它方法可行咯?当然!换成registerTypeHierarchyAdapter 就可以使用Number.class而不用一个一个的单独注册啦!
registerTypeAdapter 与 registerTypeHierarchyAdapter 的区别:
注:如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,那么必须调用Gson.toJson(Object,Type),明确告诉Gson泛型对象的类型。
三、@JsonAdapter注解 (代替registerTypeAdapter注册)
本系列的Gson注解篇,留了一个注解@JsonAdapter没讲,现在讲解。
◆◆◆ @JsonAdapter注解 (代替registerTypeAdapter注册)
----------------------------------------------------------------------------------------------------
@JsonAdapter注解就是GsonBuilder.registerTypeAdapter(Type type, Object typeAdapter)的替代形式,以注解的方式。
@JsonAdapter(UserTypeAdapter.class)本质是来代替.registerTypeAdapter(User.class, new UserTypeAdapter()),是其等价的注解形式。当使用了UserTypeAdapter来接管User.class对象的序列化与反序列化,其他的注解如@SerializedName 、@Since、@Until、@Expose失去了效果,只会调用我们实现的UserTypeAdapter.write(JsonWriter, User) 方法,我想怎么写就怎么写。
注解使用:
@JsonAdapter(UserTypeAdapter.class)public class User {private String id;@Expose private String name;@SerializedName("birt") private Date birthday;//为了代码简洁,这里移除了getter和setter方法、toString方法、构造方法等}
测试:
public class GsonTest24 {public static void main(String[] args) {User user = new User("1", "王重阳", new Date());Gson gson = new GsonBuilder().setPrettyPrinting()//格式化输出,这个配置有效//User对象序列化和序列化全都交给UserTypeAdapter了,其他配置就无效了//.registerTypeAdapter(User.class, new UserTypeAdapter())//.excludeFieldsWithoutExposeAnnotation()//因UserTypeAdapter全权接管处理,该配置无效了.create();/*** 流式序列化,使用@JsonAdapter来注册。*/String jsonString = gson.toJson(user);System.out.println("使用UserTypeAdapter:\n" + jsonString);/** 使用UserTypeAdapter:{"id": "1","name": "王重阳","birth": "2017-05-07 14:58:04"}*//*** 流式反序列化,使用@JsonAdapter来注册。*/user = gson.fromJson(jsonString, User.class);System.out.println("流式反序列化:\n" + user);/*** 流式反序列化: User [id=1, name=王重阳, birthday=Sun May 07 14:58:04 CST 2017]*/}}
结果中可以看到,当使用了TypeAdapter接管对象的序列化与反序列化后,其他的注解和配置都失效了。除了GsonBuilder.setPrettyPrinting()。
◆◆该系列的其他文章:
JSON解析类库之Gson(1) --- 简单JavaBean对象、带泛型的Bean对象与JSON互转
JSON解析类库之Gson(2) --- 泛型对象Map、List与JSON字符串互转
JSON解析类库之Gson(3) --- Gson注解
JSON解析类库之Gson(4)--- TypeAdapter接管序列化与反序列化
---------------------------------------------------------------------------------------------------
版权声明:本文为博主(chunlynn)原创文章,转载请注明出处 :http://blog.csdn.net/chenchunlin526/article/details/71173404
2 0
- JSON解析类库之Gson(4) --- TypeAdapter接管序列化与反序列化
- Gson解析之自定义序列化和反序列化
- JSON解析类库之Gson(5) --- TypeAdapter性能分析
- Gson之常见的序列化与反序列化
- Gson - Java-JSON 序列化和反序列化入门
- Gson - Java-JSON 序列化和反序列化入门
- Json工具类--使用Gson实现了Json的序列化和反序列化
- C#编程之JSON序列化与反序列化
- JSON利器之序列化与反序列化
- plist解析和JSON序列化与反序列化(JSON解析),XML解析
- Json序列化 与反序列化
- JSON序列化与反序列化
- JSON序列化与反序列化
- Json序列化与反序列化
- Json序列化与反序列化
- JSON序列化与反序列化
- JSON序列化与反序列化
- JSON序列化与反序列化
- hdoj 2586
- POJ3177【边双连通分量缩点】
- 有关Canvas图像覆盖问题
- 为什么用接口存常量是一种不良的习惯
- C语言小游戏————反弹球(简单的图形化界面)
- JSON解析类库之Gson(4) --- TypeAdapter接管序列化与反序列化
- android developer tiny share-20170506
- 重置CSS样式表
- Mac OS X配置Java环境变量
- [14]内置对象
- drizzleDumper的原理分析和使用说明
- JD-大数据竞赛心得
- Unity3d UI设计简述
- 响应式网页