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方法进行设置。也可以通过构造方法进行设置。