博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JSON解析类库之Gson(4) --- TypeAdapter接管序列化与反序列化
阅读量:2382 次
发布时间:2019-05-10

本文共 27253 字,大约阅读时间需要 90 分钟。

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

---Gson类库学习, 生成与解析json数据,json字符串与对象互转

一、前言
本文介绍高级的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
 {
  /**
   * 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
 {
    /**
     * 流式序列化(速度快),将序列化后的值写到流对象中
     */
    @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
> userListTypeAdapter = new TypeAdapter
>() {
// 序列化[1]
            @Override
            public void write(JsonWriter out, List
 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
 read(JsonReader in) throws IOException {                 List
 listUsers = new ArrayList
();                 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
 userList = new ArrayList
();         userList.add(user1);         userList.add(user2);         userList.add(user3); // 泛型的类型,序列化和反序列时都要带上该参数         Type type = new TypeToken
>() { }.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
 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
泛型对象的序列化与反序列化
 * 

 流式处理,高效率,任何类型都可以处理

 * 
 * @ClassName UserListTypeAdapter 
 * @author chunlynn
 * @date 2017年5月6日
 * @Version V1.0
 */
public class UserListTypeAdapter extends TypeAdapter
> {
    /**
     * 流式序列化(速度快),将序列化后的值写到流中,需要注意开启流、关闭流
     */
    @Override
    public void write(JsonWriter out, List
 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
 read(JsonReader in) throws IOException {         List
 listUsers = new ArrayList
();         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
() {
            @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
 numberJsonSerializer = new JsonSerializer
() {
            @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注册)
本系列的,留了一个注解@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()。
该系列的其他文章
---------------------------------------------------------------------------------------------------
版权声明:本文为博主(chunlynn)原创文章,转载请注明出处 :

你可能感兴趣的文章
一种自适应的柔性制造系统优化调度算法
查看>>
现代管理思想与总图设计
查看>>
原创BPR之企业流程分析模型图 FDD
查看>>
PLM技术促进现代模具企业精益化和规模化
查看>>
独一无二的IFS CAD与PDM集成工具发布
查看>>
BPR-FDD 模型图原始档
查看>>
mail
查看>>
团队管理的五项职能--学习笔记加个人理解总结
查看>>
自勉三句话--关于职业生涯规划
查看>>
grace
查看>>
test
查看>>
用友实施方法论
查看>>
系统功能清单
查看>>
ERP&MES&SCM 三兄弟发展史
查看>>
Grace的简历-v3.1
查看>>
file2
查看>>
file456
查看>>
需求定位模型
查看>>
BOM
查看>>
Eclipse Debug调试详解
查看>>