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> {
    /**
     * 流式序列化(速度快),将序列化后的值写到流对象中
     */
    @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 的区别:

registerTypeAdapterregisterTypeHierarchyAdapter支持泛型支持继承
注:如果一个被序列化的对象本身就带有泛型,且注册了相应的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
原创粉丝点击