`

Gson第四讲

    博客分类:
  • json
阅读更多

本次文章的主要内容:

  • TypeAdapter
  • JsonSerializer与JsonDeserializer
  • TypeAdapterFactory
  • @JsonAdapter注解
  • TypeAdapter与 JsonSerializer、JsonDeserializer对比
  • TypeAdapter实例
  • 结语
  • 后期预告

一、TypeAdapter

TypeAdapter 是Gson自2.0(源码注释上说的是2.1)开始版本提供的一个抽象类,用于接管某种类型的序列化和反序列化过程,包含两个注要方法 write(JsonWriter,T) 和 read(JsonReader) 其它的方法都是final方法并最终调用这两个抽象方法。

public abstract class TypeAdapter<T> {
    public abstract void write(JsonWriter out, T value) throws IOException;
    public abstract T read(JsonReader in) throws IOException;
    //其它final 方法就不贴出来了,包括`toJson`、`toJsonTree`、`toJson`和`nullSafe`方法。
}

注意:TypeAdapter 以及 JsonSerializer 和 JsonDeserializer 都需要与 GsonBuilder.registerTypeAdapter 示或GsonBuilder.registerTypeHierarchyAdapter配合使用,下面将不再重复说明。

使用示例:

User user = new User("怪盗kidou", 24);
user.emailAddress = "ikidou@example.com";
Gson gson = new GsonBuilder()
        //为User注册TypeAdapter
        .registerTypeAdapter(User.class, new UserTypeAdapter())
        .create();
System.out.println(gson.toJson(user));

UserTypeAdapter的定义:

public class UserTypeAdapter extends TypeAdapter<User> {

    @Override
    public void write(JsonWriter out, User value) throws IOException {
        out.beginObject();
        out.name("name").value(value.name);
        out.name("age").value(value.age);
        out.name("email").value(value.email);
        out.endObject();
    }

    @Override
    public User read(JsonReader in) throws IOException {
        User user = new User();
        in.beginObject();
        while (in.hasNext()) {
            switch (in.nextName()) {
                case "name":
                    user.name = in.nextString();
                    break;
                case "age":
                    user.age = in.nextInt();
                    break;
                case "email":
                case "email_address":
                case "emailAddress":
                    user.email = in.nextString();
                    break;
            }
        }
        in.endObject();
        return user;
    }
}

当我们为User.class 注册了 TypeAdapter之后,只要是操作User.class 那些之前介绍的@SerializedName 、FieldNamingStrategySinceUntilExpos通通都黯然失色,失去了效果,只会调用我们实现的UserTypeAdapter.write(JsonWriter, User) 方法,我想怎么写就怎么写。

再说一个场景,在该系列的第一篇文章就说到了Gson有一定的容错机制,比如将字符串 "24" 转成int 的24,但如果有些情况下给你返了个空字符串怎么办(有人给我评论问到这个问题)?虽然这是服务器端的问题,但这里我们只是做一个示范。

int型会出错是吧,根据我们上面介绍的,我注册一个TypeAdapter 把 序列化和反序列化的过程接管不就行了?

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Integer.class, new TypeAdapter<Integer>() {
            @Override
            public void write(JsonWriter out, Integer value) throws IOException {
                out.value(String.valueOf(value)); 
            }
            @Override
            public Integer read(JsonReader in) throws IOException {
                try {
                    return Integer.parseInt(in.nextString());
                } catch (NumberFormatException e) {
                    return -1;
                }
            }
        })
        .create();
System.out.println(gson.toJson(100)); // 结果:"100"
System.out.println(gson.fromJson("\"\"",Integer.class)); // 结果:-1

注:测试空串的时候一定是"\"\""而不是""""代表的是没有json串,"\"\""才代表json里的""

你说这一接管就要管两样好麻烦呀,我明明只想管序列化(或反列化)的过程的,另一个过程我并不关心,难道没有其它更简单的方法么? 当然有!就是接下来要介绍的JsonSerializer与JsonDeserializer

二、JsonSerializer与JsonDeserializer

JsonSerializer 和JsonDeserializer 不用像TypeAdapter一样,必须要实现序列化和反序列化的过程,你可以据需要选择,如只接管序列化的过程就用 JsonSerializer ,只接管反序列化的过程就用 JsonDeserializer ,如上面的需求可以用下面的代码。

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Integer.class, new JsonDeserializer<Integer>() {
            @Override
            public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                try {
                    return json.getAsInt();
                } catch (NumberFormatException e) {
                    return -1;
                }
            }
        })
        .create();
System.out.println(gson.toJson(100)); //结果:100
System.out.println(gson.fromJson("\"\"", Integer.class)); //结果-1

下面是所有数字都转成序列化为字符串的例子

JsonSerializer<Number> numberJsonSerializer = new JsonSerializer<Number>() {
    @Override
    public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(String.valueOf(src));
    }
};
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.classdouble.class是行不通的。同时不能使用父类来替上面的子类型,这也是为什么要分别注册而不直接使用Number.class的原因。

上面特别说明了registerTypeAdapter不行,那就是有其它方法可行咯?当然!换成registerTypeHierarchyAdapter 就可以使用Number.class而不用一个一个的当独注册啦!

registerTypeAdapter与registerTypeHierarchyAdapter的区别:

  registerTypeAdapter registerTypeHierarchyAdapter
支持泛型
支持继承

注:如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。

Type type = new TypeToken<List<User>>() {}.getType();
TypeAdapter typeAdapter = new TypeAdapter<List<User>>() {
   //略
};
Gson gson = new GsonBuilder()
        .registerTypeAdapter(type, typeAdapter)
        .create();
List<User> list = new ArrayList<>();
list.add(new User("a",11));
list.add(new User("b",22));
//注意,多了个type参数
String result = gson.toJson(list, type);

三、TypeAdapterFactory

TypeAdapterFactory,见名知意,用于创建TypeAdapter的工厂类,通过对比Type,确定有没有对应的TypeAdapter,没有就返回null,与GsonBuilder.registerTypeAdapterFactory配合使用。

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new TypeAdapterFactory() {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            return null;
        }
    })
    .create();

四、@JsonAdapter注解

JsonAdapter相较之前介绍的SerializedName 、FieldNamingStrategySinceUntilExpos这几个注解都是比较特殊的,其它的几个都是用在POJO的字段上,而这一个是用在POJO类上的,接收一个参数,且必须是TypeAdpaterJsonSerializerJsonDeserializer这三个其中之一。

上面说JsonSerializerJsonDeserializer都要配合GsonBuilder.registerTypeAdapter使用,但每次使用都要注册也太麻烦了,JsonAdapter就是为了解决这个痛点的。

使用方法(以User为例):

@JsonAdapter(UserTypeAdapter.class) //加在类上
public class User {
    public User() {
    }
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    public String name;
    public int age;
    @SerializedName(value = "emailAddress")
    public String email;
}

使用时不用再使用 GsonBuilder去注册UserTypeAdapter了。
注:@JsonAdapter 仅支持 TypeAdapterTypeAdapterFactory

Gson gson = new Gson();
User user = new User("怪盗kidou", 24, "ikidou@example.com");
System.out.println(gson.toJson(user));
//结果:{"name":"怪盗kidou","age":24,"email":"ikidou@example.com"}
//为区别结果,特意把email字段与@SerializedName注解中设置的不一样

注意:JsonAdapter的优先级比GsonBuilder.registerTypeAdapter的优先级更高。

五、TypeAdapter与 JsonSerializer、JsonDeserializer对比

  TypeAdapter JsonSerializer、JsonDeserializer
引入版本 2.0 1.x
Stream API 支持 不支持*,需要提前生成JsonElement
内存占用 TypeAdapter
效率 TypeAdapter
作用范围 序列化  反序列化 序列化  反序列化

六、TypeAdapter实例

注:这里的TypeAdapter泛指TypeAdapterJsonSerializerJsonDeserializer
这里的TypeAdapter 上面讲了一个自动将 字符串形式的数值转换成int型时可能出现 空字符串的问题,下面介绍一个其它读者的需求:

服务器返回的数据中data字段类型不固定,比如请求成功data是一个List,不成功的时候是String类型,这样前端在使用泛型解析的时候,怎么去处理呢?

其实这个问题的原因主要由服务器端造成的,接口设计时没有没有保证数据的一致性,正确的数据返回姿势:同一个接口任何情况下不得改变返回类型,要么就不要返,要么就返空值,如null[],{}

但这里还是给出解决方案:
方案一:

Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
    @Override
    public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonArray()){
            //这里要自己负责解析了
            Gson newGson = new Gson();
            return newGson.fromJson(json,typeOfT);
        }else {
            //和接口类型不符,返回空List
            return Collections.EMPTY_LIST;
        }
    }
}).create();

方案二:

 Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(List.class, new JsonDeserializer<List<?>>() {
    @Override
    public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonArray()) {
            JsonArray array = json.getAsJsonArray();
            Type itemType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
            List list = new ArrayList<>();
            for (int i = 0; i < array.size(); i++) {
                JsonElement element = array.get(i);
                Object item = context.deserialize(element, itemType);
                list.add(item);
            }
            return list;
        } else {
            //和接口类型不符,返回空List
            return Collections.EMPTY_LIST;
        }
    }
}).create();

要注意的点:

  • 必须使用registerTypeHierarchyAdapter方法,不然对List的子类无效,但如果POJO中都是使用List,那么可以使用registerTypeAdapter
  • 对于是数组的情况,需要创建一个新的Gson,不可以直接使用context,不然gson又会调我们自定义的JsonDeserializer造成递归调用,方案二没有重新创建Gson,那么就需要提取出List<E>中E的类型,然后分别反序列化适合为E手动注册了TypeAdaper的情况。
  • 从效率上推荐方案二,免去重新实例化Gson和注册其它TypeAdapter的过程。

原文链接:http://www.jianshu.com/p/3108f1e44155

分享到:
评论

相关推荐

    om.google.gson.Gson Gson maven依赖

    4. **字段忽略**:通过注解`@Expose`可以控制哪些字段被序列化或反序列化。 5. **类型安全的转换**:Gson的`fromJson()`方法支持传入`Type`或`TypeToken`参数,以确保反序列化的对象类型正确。 6. **流式API**:...

    GSON JAR包 最新包和历史包 gson-2.10.1

    介绍:Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. 压缩包里有以下文件: ...

    com.google.gson.Gson 2.8.1 2.8.2 jar包 gson

    4. **流式API**:对于大量数据处理,Gson提供了流式API,允许开发者逐行读取或写入JSON,以减少内存消耗。 5. **日期和时间处理**:Gson提供了内置的日期和时间解析策略,可以将日期对象转换为多种格式的JSON字符串...

    gson jar包下载

    **四、Gson的安装与引入** Gson作为独立的JAR包,可以通过Maven或Gradle等构建工具添加依赖。对于Maven,可以在pom.xml中添加以下依赖: ```xml &lt;groupId&gt;com.google.code.gson&lt;/groupId&gt; &lt;artifactId&gt;gson ...

    json解析 gson fastjson

    使用Gson,你可以通过`Gson().toJson()`方法将Java对象转化为JSON字符串,通过`new Gson().fromJson()`方法将JSON字符串反序列化为Java对象。 例如: ```java Person person = new Person("John", "Doe"); String ...

    gson-2.8.5版本的jar包

    GSON简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。 Gson是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个JSON字符串...

    Gson的2.2.4jar

    4. **自定义序列化和反序列化**:用户可以通过实现`JsonSerializer`和`JsonDeserializer`接口来自定义对象的转换规则,以适应特定需求。 5. **字段映射**:Gson允许通过注解(如`@SerializedName`)来指定JSON字段...

    com.google.gson.Gson.jar

    json我们在网络请求中经常用到,最近最火的微信小程序开发中... 就会报错,因为对于嵌套类型的json它是解释不出来的,这时就需要用到com.google.gson.Gson.jar了。资源中提供了jar包与代码示例,一句代码解决您的问题。

    googleGson JsonObject json转换包

    包含以下java源文件: com.google.gson.DefaultDateTypeAdapter.class com.google.gson.ExclusionStrategy.class com.google.gson.FieldAttributes.class com.google.gson.FieldNamingPolicy.class ...

    gson 使用Gson将Java对象转换为JSON

    Gson是Google的一个开源项目,可以将Java对象转换成JSON,也可能将JSON转换成Java对象。 Gson里最重要的对象有2个Gson 和 GsonBuilder Gson有2个最基本的方法 1) toJson() – 转换java 对象到JSON 2) from...

    gson-2.8.0-API文档-中文版.zip

    赠送jar包:gson-2.8.0.jar; 赠送原API文档:gson-2.8.0-javadoc.jar; 赠送源代码:gson-2.8.0-sources.jar; 赠送Maven依赖信息文件:gson-2.8.0.pom; 包含翻译后的API文档:gson-2.8.0-javadoc-API文档-中文...

    Google-Gson 2.2.4

    4. **复杂类型处理**:Gson可以处理各种复杂的Java类型,包括数组、集合、Map、枚举以及嵌套对象等。 5. **注解支持**:通过使用注解,开发者可以精确控制哪些字段被转换,以及如何转换。 6. **流式API**:Gson...

    gson-2.8.6-API文档-中文版.zip

    赠送jar包:gson-2.8.6.jar; 赠送原API文档:gson-2.8.6-javadoc.jar; 赠送源代码:gson-2.8.6-sources.jar; 赠送Maven依赖信息文件:gson-2.8.6.pom; 包含翻译后的API文档:gson-2.8.6-javadoc-API文档-中文...

    Gson-2.8.1的官方jar包合集【gson-2.8.1.jar,gson-2.8.1-sources.jar】

    Gson是Google开发的一款强大的Java库,用于将Java对象转换为JSON格式的字符串,以及将JSON数据解析回等效的Java对象。这个压缩包包含了Gson库的版本2.8.1,它提供了两个主要的组件:`gson-2.8.1.jar`和`gson-2.8.1-...

    gson的jar包

    Gson是Google开发的一款Java库,用于在Java对象和JSON数据之间进行映射。这个库使得Java对象可以被转换为JSON格式的字符串,同时也可以将JSON文本解析为等效的Java对象,极大地简化了JSON数据的处理。在服务端开发中...

    gson 需要的jar

    Gson是Google开发的一款Java库,它能够将Java对象转换为...正确引入Gson的jar文件,无论是通过手动添加还是构建工具配置,都是使用Gson库的第一步。通过熟练掌握Gson,你可以提升项目中JSON数据处理的效率和灵活性。

    Gson简要使用笔记

    Gson 是 Google 推出的一款 Java 语言编写的 JSON 库,它允许开发者将 Java 对象转换成 JSON 格式的字符串,反之亦然。Gson 库的强大之处在于其灵活性和简洁性,使得 JSON 序列化和反序列化变得更加简单。 首先,让...

    gsondemo解析json

    在Java开发中,Gson库是Google提供的一款强大的JSON处理工具,它允许我们将Java对象转换为JSON字符串,反之亦然。本示例"GsonDemo"将深入探讨如何使用Gson库来解析JSON数据。JSON(JavaScript Object Notation)是一...

    gson-2.8.6.jar下载

    4. 支持基本数据类型及其封装类型,Collection类型,Date类型及自定义类型。 5. 支持将JSON数组转换为Java数组或List,支持泛型。 6. 支持将Java的Map转换为JSONObject以及相反的转换。 7. 支持将null值和空值转换为...

    Google gson jar包

    标题"Google gson jar包"暗示了我们讨论的是Google Gson库的Java档案文件(JAR),它包含了所有必要的类和资源,使开发者能够在项目中直接使用Gson功能。JAR文件通常在Java开发中用于封装库,以便在应用程序中作为...

Global site tag (gtag.js) - Google Analytics