JSON解析类库之Gson(4) --- TypeAdapter接管序列化与反序列化

xiaoxiao2021-02-28  89

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> {     /**      * 流式序列化(速度快),将序列化后的值写到流对象中      */     @Override     public 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流对象中读取      */     @Override     public 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]             @Override             public 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]             @Override             public 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>> {     /**      * 流式序列化(速度快),将序列化后的值写到流中,需要注意开启流、关闭流      */     @Override     public 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       */     @Override     public 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"}] 这里需要注意的是,如果没有调用 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>() {             @Override             public 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)); //结果:100 System.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>() {             @Override             public 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 的区别: 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

转载请注明原文地址: https://www.6miu.com/read-65593.html

最新回复(0)