美文网首页Android 库集JavaAndroid开发精选
你真的会用Gson吗?Gson使用指南(四)

你真的会用Gson吗?Gson使用指南(四)

作者: 怪盗kidou | 来源:发表于2016-04-05 16:11 被阅读38490次

    本文为作者根据日常使用结合Gson源码注释及wiki所作的原创内容,转载请注明出处。

    该系列其它文章

    注:此系列基于Gson 2.4。

    本次文章的主要内容:

    • 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 那些之前介绍的@SerializedNameFieldNamingStrategySinceUntilExpos通通都黯然失色,失去了效果,只会调用我们实现的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

    JsonSerializerJsonDeserializer 不用像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相较之前介绍的SerializedNameFieldNamingStrategySinceUntilExpos这几个注解都是比较特殊的,其它的几个都是用在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的过程。

    结语

    Gson系列总算是完成了,感觉写得越来越差了,我怕我写得太啰嗦,也不能总是大片大片的贴代码,所以可能有的地方写得并不详细,排版也不美观,但都些都不重点,重点是Gson里我们能用上的都一一介绍一遍,只要你确确实实把我这几篇文章上的内容都学会的话,以后Gson上的任何问题都不再是问题,当然可能很多内容对于实际的开发中用的并不多,但下次有什么疑难杂症就难不倒你了。

    本系列不提供Demo源码,最重要的是自己实验。

    写一篇文章还是要花不少时间和精力,要写示例、调式、组织语言、码字等等,加上关注的人在慢慢的增加的同时既给了我动力也给我不少压力,如有纰漏或者更好的例子都可以和我交流。

    后期预告:

    之前有人给我评论说 出一点 retrofit 相关内容,我想了想,出是会出,但在此之前我想先出大概3~4篇文章用于介绍 泛型、反射、注解和HTTP 的相关内容,当你确实掌握之后,我打包票你只需要看一遍Retrofit官方教程的代码示例,都不用看其它英文说明,就可以轻松玩转Retrofit。不服来战!

    相关文章

      网友评论

      • bhfo:只有熟读这篇,才不被奇怪的后台干翻!
      • 杨晓是大V:超级喜欢老大的笔记
      • Lindroid:第五节中的表格好像Markdown格式有问题,表格没有显示出来。
        Lindroid:@怪盗kidou 呃,这一段还是有问题:
        ||TypeAdapter| JsonSerializer、JsonDeserializer|
        |--|--|--|--|
        |引入版本|2.0|1.x|
        |Stream API|支持|不支持*,需要提前生成JsonElement|
        |内存占用|小|比TypeAdapter大|
        |效率|高|比TypeAdapter低|
        |作用范围|序列化 和 反序列化|序列化 或 反序列化|
        怪盗kidou:@Lindroid 修复了,可能简书那边修改了一下渲染引擎,以前显示没有问题
      • 83a29f8de63a:请问作者大大,这要怎么调用啊?(TypeSuper是一个空类,分别有六个不同种类的数组的类分别继承TypeSuper)
        public class TypeSuperDeserializer implements JsonDeserializer<TypeSuper> {
        private static final String HorizontalScrollCard = "horizontalScrollCard";
        private static final String Video = "video";
        private static final String TextFooter = "textFooter";
        private static final String VideoCollectionWithCover = "videoCollectionWithCover";
        private static final String TextHeader = "textHeader";
        private static final String VideoCollectionOfFollow = "videoCollectionOfFollow";

        @Override
        public TypeSuper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)throws JsonParseException{
        JsonObject jsonObject = json.getAsJsonObject();
        //获取里面的type字段,用作区分的标志
        String type = jsonObject.get("type").getAsString();
        if (type.equals(HorizontalScrollCard))
        return new Gson().fromJson(json, HorizontalScrollCard.class);
        if (type.equals("video"))
        return new Gson().fromJson(json, Video.class);
        if (type.equals(TextFooter))
        return new Gson().fromJson(json, TextFooter.class);
        if (type.equals(VideoCollectionWithCover))
        return new Gson().fromJson(json, VideoCollectionWithCover.class);
        if (type.equals(TextHeader))
        return new Gson().fromJson(json, TextHeader.class);
        if (type.equals(VideoCollectionOfFollow))
        return new Gson().fromJson(json, VideoCollectionOfFollow.class);
        else
        return null;
        }
        }
        怪盗kidou:@1649520857 这个时候你不就是直接用 TypeSuper了么?如果你都知道要用那个子类了,刚刚的那个就Adapter就没有意义了
        83a29f8de63a:@怪盗kidou
        但是具体怎么区分并使用return new Gson().fromJson(json, xxx.class);的不同数据呢?
        怪盗kidou:@1649520857 如果没有理解错你的需求的话用 gsonBuilder. registerTypeHierarchyAdapter(TypeSuper.class, new TypeSuperDeserializer())
      • supersugar:受益匪浅,感谢老司机带路~~
        有个问题请大神指教一下
        pojo如下定义:
        public class User {
        public String name;
        public int age;
        public String emailAddress;
        public String phone;
        }
        server端返回json数据,可能出于各种原因,返回的字段少了,比如少了phone,或少了name,像这样:
        {
        "name": "怪盗kidou",
        "age": 24,
        "emailAddress": "ikidou_1@example.com"
        }
        这种情况假设说是不确定的,任意一个接口,任意一个字段都可能少传
        那么反序列化完之后,在调用pojo对象的时候,比如user.phone或user.name就会抛空指针异常,这样总不能取每个属性的时候都校验一下吧?
        4篇文章都看了,但是没找到解决办法,也可能有但是鄙人愚钝并未发现,希望司机大哥看到了帮忙解答一下吧,不胜感激~~~
        supersugar:谢谢,这个问题让我查到一个相关的问题:pojo应该使用封装类不使用基本类型.
        如果真是有约定好的某个非空字段server端未返回,那么要从问题根源上处理,也就是server端修改
        怪盗kidou:@supersugar 你应该是需要做字段校验的啊,如果你的业务上只是 if (user.name == null) return "" 这种的话你可以直接给字段默认值,这样的话没有的字段不会影响,但是如果后台返回的是
        {
        "phone":null
        }
        这种的话就没有办法了,gson没有提供默认值或者校验的API
      • Cloverss:您好,在下有个疑问,在这里请教一下

        是这样,如果我有两个对象 比如说是 A 和 B,A 和 B 中都有 各自的成员变量,我如果将这两个对象合并为一个json呢?

        比如 A 中有 String name,String pwd ;
        B 中有 String color,String size;

        我如何得到
        {
        "name":"google",
        "pwd":"123",
        "color","red",
        "size","20"
        }
        或者还是我的思路不对,应该用其他的方式来实现?新手来请教一下,打扰到您,请见谅
        怪盗kidou:@Cloverss 你是想生成的时候啊,那不行
        Cloverss:@怪盗kidou 什么啊? 我如何得到
        {
        "name":"google",
        "pwd":"123",
        "color","red",
        "size","20"
        }
        这样的形式啊? Gson 中好像没有合并的方法,只能够自己拼接吗? 太菜了,求教一哈
        怪盗kidou:@Cloverss 对的,可以这么的
      • 澹台舍予:后台是php写的,当请求成功时,返回{
        "code": 10000,
        "msg": "成功",
        "data": { }
        };当请求失败,返回{
        "code": 10001,
        "msg": "失败",
        "data": []
        }; 针对这样的情况,怎么做解析的封装?
        怪盗kidou:可以看看文章后面的评论,都有回复
      • BeJack:很不理解,到现在GSON的解析为什么不出一个判空的注解呢,某个键值为null或者类型不匹配就自动返回null,其他字段不影响解析
        怪盗kidou:@BeJack 我反倒是觉得老是返回一些错误的类型是不可以理解的,返回null没有问题
      • Professioner:CustomTypeAdapter<T>示例:

        第二部分

        @SuppressWarnings("unchecked")
        @Override
        public T read(JsonReader in) throws IOException {

        if( in.peek().equals(JsonToken.BEGIN_ARRAY)){
        List<User> list = new ArrayList<User>();

        in.beginArray();
        while (in.hasNext()) {
        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.emailAddress = in.nextString();
        break;
        }
        }
        in.endObject();
        list.add(user);
        }
        in.endArray();
        return (T) list;
        }else{
        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.emailAddress = in.nextString();
        break;
        }
        }
        in.endObject();
        return (T) user;
        }

        }

        }
      • Professioner:CustomTypeAdapter<T>示例:

        第一部分

        package com.ueuo.gson;

        import java.io.IOException;
        import java.util.ArrayList;
        import java.util.List;

        import com.google.gson.TypeAdapter;
        import com.google.gson.stream.JsonReader;
        import com.google.gson.stream.JsonToken;
        import com.google.gson.stream.JsonWriter;

        public class CustomTypeAdapter<T> extends TypeAdapter<T>{

        @SuppressWarnings("unchecked")
        @Override
        public void write(JsonWriter out, T paramT) throws IOException {
        if(paramT instanceof User){
        out.beginObject();
        User user = (User)paramT;
        out.name("name").value(user.name);
        out.name("age").value(user.age);
        out.name("email").value(user.emailAddress);
        out.endObject();
        }else if(paramT instanceof List<?>){
        List<User> users = (List<User>)paramT;
        out.beginArray();
        for (User value : users) {
        out.beginObject();
        out.name("name").value(value.name);
        out.name("age").value(value.age);
        out.name("email").value(value.emailAddress);
        out.endObject();
        }
        out.endArray();
        }
        }
      • Professioner:示例( 因为太长分几部分):

        第七部分:

        System.out.println("\n方案二:");
        Gson gson10 = 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();

        List<User> users10 = gson10.fromJson(result2, type);
        for(User user10 : users10){
        System.out.println(user10.name+"; "+user10.age+"; "+user10.emailAddress);
        }
        List<User> users11 = gson10.fromJson("\"\"", type);
        System.out.println(users11);

        }
        }
      • Professioner:示例( 因为太长分几部分):

        第六部分:

        System.out.println("\n四、@JsonAdapter注解");
        System.out.println("\n使用@JsonAdapter注解");
        Gson gson7 = new Gson();
        User7 user7 = new User7("怪盗kidou", 24, "ikidou@example.com");
        System.out.println(gson7.toJson(user7));


        System.out.println("\n六、TypeAdapter实例");
        Gson gson8 = 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();

        System.out.println("\n方案一:");
        List<User> users8 = gson8.fromJson(result2, type);
        for(User user8 : users8){
        System.out.println(user8.name+"; "+user8.age+"; "+user8.emailAddress);
        }
        List<User> users9 = gson8.fromJson("\"\"", type);
        System.out.println(users9);
      • Professioner:示例( 因为太长分几部分):

        第五部分:

        System.out.println("\n三、TypeAdapterFactory");
        System.out.println("\n自定义泛型TypeAdapter -- CustomTypeAdapter<T>");
        Gson gson6 = new GsonBuilder()
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        return new CustomTypeAdapter<T>();
        }
        })
        .create();

        List<User> list2 = new ArrayList<User>();
        list2.add(new User("a", 11));
        list2.add(new User("b", 22));
        System.out.println("\n序列化");
        String result2 = gson6.toJson(list2, type);
        System.out.println(result2);
        System.out.println("\n反序列化");
        users = gson6.fromJson(result2, type);
        for (User u : users) {
        System.out.println(u.name+"; "+u.age+"; "+u.emailAddress);
        }

        System.out.println("\n序列化");
        String result3 = gson6.toJson(new User("c", 33), User.class);
        System.out.println(result3);

        System.out.println("\n反序列化");
        User user3 = gson6.fromJson(result3,User.class);
        System.out.println(user3.name+"; "+user3.age+"; "+user3.emailAddress);
      • Professioner:示例( 因为太长分几部分):

        第四部分:

        @Override
        public List<User> read(JsonReader in) throws IOException {
        List<User> list = new ArrayList<User>();

        in.beginArray();
        while (in.hasNext()) {
        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.emailAddress = in.nextString();
        break;
        }
        }
        in.endObject();
        list.add(user);
        }
        in.endArray();

        return list;
        }
        };
        Gson gson5 = new GsonBuilder()
        .registerTypeAdapter(type, typeAdapter)
        .create();
        List<User> list = new ArrayList<User>();
        list.add(new User("a", 11));
        list.add(new User("b", 22));
        // 注意,多了个type参数
        System.out.println("如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,\n那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。");
        System.out.println("\n序列化");
        String result = gson5.toJson(list, type);
        System.out.println(result);

        System.out.println("\n反序列化");
        List<User> users = gson5.fromJson(result, new TypeToken<List<User>>() {}.getType());
        for (User u : users) {
        System.out.println(u.name+"; "+u.age+"; "+u.emailAddress);
        }
      • Professioner:示例( 因为太长分几部分):

        第三部分:

        System.out.println("\n所有数字都转成序列化为字符串的例子");
        JsonSerializer<Number> numberJsonSerializer = new JsonSerializer<Number>() {
        @Override
        public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(String.valueOf(src));
        }
        };
        Gson gson4 = new GsonBuilder().registerTypeAdapter(Integer.class, numberJsonSerializer)
        .registerTypeAdapter(Long.class, numberJsonSerializer)
        .registerTypeAdapter(Float.class, numberJsonSerializer)
        .registerTypeAdapter(Double.class, numberJsonSerializer).create();
        System.out.println(gson4.toJson(100.0f));// 结果:"100.0"
        System.out.println(gson4.toJson(100));// 结果:"100"
        System.out.println(gson4.toJson(100.0d));// 结果:"100.0"
        System.out.println(gson4.toJson(0x64));// 结果:"100"


        System.out.println("\nTypeAdapter<List<User>>");
        Type type = new TypeToken<List<User>>() {
        }.getType();
        TypeAdapter<List<User>> typeAdapter = new TypeAdapter<List<User>>() {

        @Override
        public void write(JsonWriter out, List<User> paramT) throws IOException {
        out.beginArray();
        for (User value : paramT) {
        out.beginObject();
        out.name("name").value(value.name);
        out.name("age").value(value.age);
        out.name("email").value(value.emailAddress);
        out.endObject();
        }
        out.endArray();
        }
      • Professioner:示例( 因为太长分几部分):

        第二部分:

        System.out.println();
        User user2 = gson.fromJson(stringjson, User.class);
        System.out.println(user2.name);
        System.out.println(user2.age);
        System.out.println(user2.emailAddress);

        System.out.println("\n注册一个TypeAdapter序列化和反序列化空字符串");
        Gson gson2 = 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(gson2.toJson(100)); // 结果:"100"
        System.out.println(gson2.fromJson("\"\"", Integer.class)); // 结果:-1

        System.out.println("\n二、JsonSerializer与JsonDeserializer");

        System.out.println("\n只接管反序列化的过程就用 JsonDeserializer");
        Gson gson3 = 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(gson3.toJson(100)); // 结果:100
        System.out.println(gson3.fromJson("\"\"", Integer.class)); // 结果-1
      • Professioner:示例( 因为太长分几部分):

        第一部分:

        package com.ueuo.gson;

        import java.io.IOException;
        import java.lang.reflect.ParameterizedType;
        import java.lang.reflect.Type;
        import java.util.ArrayList;
        import java.util.Collections;
        import java.util.List;
        import com.google.gson.Gson;
        import com.google.gson.GsonBuilder;
        import com.google.gson.JsonArray;
        import com.google.gson.JsonDeserializationContext;
        import com.google.gson.JsonDeserializer;
        import com.google.gson.JsonElement;
        import com.google.gson.JsonParseException;
        import com.google.gson.JsonPrimitive;
        import com.google.gson.JsonSerializationContext;
        import com.google.gson.JsonSerializer;
        import com.google.gson.TypeAdapter;
        import com.google.gson.TypeAdapterFactory;
        import com.google.gson.reflect.TypeToken;
        import com.google.gson.stream.JsonReader;
        import com.google.gson.stream.JsonWriter;

        /**
        * 四、你真的会用Gson吗?Gson使用指南(四)
        *
        * http://www.jianshu.com/p/3108f1e44155
        *
        * @Author xbn 2017.11.21
        */
        public class UsageGsonFour {

        public static void main(String[] args) throws Exception {

        System.out.println("\n一、TypeAdapter");
        System.out.println("\n为User注册TypeAdapter");
        User user = new User("怪盗kidou", 24);
        user.emailAddress = "ikidou@example.com";
        Gson gson = new GsonBuilder()
        // 为User注册TypeAdapter
        .registerTypeAdapter(User.class, new UserTypeAdapter()).create();
        String stringjson = gson.toJson(user);
        System.out.println(stringjson);
      • 貌似善良:写的真的很棒,又复习了下.说个遇到的情况Gson在反序列化Timestamp类型时会抛异常,就可以用到registerTypeAdapterFactory,接管Timestamp类型的反序列化转换.
        private static GsonBuilder gsonBulder = new GsonBuilder();
        gsonBulder.registerTypeAdapter(java.sql.Timestamp.class, JsonUtil.TIMESTAMP);

        public static final TypeAdapter<java.sql.Timestamp> TIMESTAMP = new TypeAdapter<java.sql.Timestamp>() {
        @Override
        public java.sql.Timestamp read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
        }
        try {
        return new java.sql.Timestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(in.nextString()).getTime());
        } catch (Exception e) {
        throw new JsonSyntaxException(e);
        }
        }

        @Override
        public void write(JsonWriter out, java.sql.Timestamp value) throws IOException {
        out.value(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value));
        }
        };
        希望对大家有帮助.
      • Terry:如果本地是个对象,服务器返回的时候空值得时候是个空数组,有值得时候个对象,怎么解决呢?
        怪盗kidou:没太看懂你的意思,和文中的第六点有什么区别么
      • 18b4eaf9eced:《你真的会用Gson吗?Gson使用指南(四) - 简书》写的挺不错的,已经收藏了。

        源码解析:http://tinyurl.com/ycqn86hj


        aec055493426:写的不错,谢谢博主;已 收 藏~
      • 60530898bdc3:TypeAdapterFactory,见名知意,用于创建TypeAdapter的工厂类,通过对比Type,确定有没有对应的TypeAdapter,没有就返回null,与GsonBuilder.registerTypeAdapterFactory配合使用。这个判断过程能不能贴一下代码
        60530898bdc3:@怪盗kidou 还有很多情况不行,如System.out.println(gson.fromJson("\"\"",Integer.class)); // 结果:-1在你的demo里肯定是可以的,但是当一个类的某个属性定义为int,但是服务器返回这个字段为null就不行,返回5.5或者空字符串都可以。对于复杂的对象还是没有一个通用的解决办法。
        60530898bdc3:@怪盗kidou 每个class写一个??还有各种内部类也要处理咋办
        怪盗kidou:就用 TypeToken获取,目标类型的信息,如果是你想要的类型,你就返回你写的TypeAdapter就行了,不是你想要的就返回null,你可以看看Gson内置的源码, 有个叫 CollectionTypeAdapterFactory 的类,里面create 方法
      • AutuWat:特意登陆来关注.
        怪盗kidou:@AutuWat :+1:
      • 爱吃鱼的外星人:请问下混淆怎么处理?
        这我的GSON混淆
        -keepattributes Signature
        # For using GSON @Expose annotation
        -keepattributes *Annotation*
        # Gson specific classes
        -keep class sun.misc.Unsafe { *; }
        -keep class com.google.gson.stream.** { *; }
        # Application classes that will be serialized/deserialized over Gson
        #这句非常重要,主要是滤掉 com.demo.demo.bean包下的所有.class文件不进行混淆编译,com.demo.demo是你的包名
        -keep class com.xxx.xxx.model** {*;}
        -keepclassmembers class com.xxx.xxx.model** { <fields>; }

        试了很多种还是报下面这个错误
        at com.google.gson.internal.bind.TypeAdapters$26.a(SourceFile:762)
        怪盗kidou:@爱吃鱼的外星人 你没有在字段上使用 SerializedName 注解吧,还有你也没说,是什么情况下出的这个问题,是用了 TypeAdapter/JsonSerializer/JsonDeserializer 了么?

        可以按Gson 官方的配置配,如果你用了 SerializedName 注解的话 就可以不用加 你的model包下的那些类
        https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg
      • ramblejoy:写的详细 帮了大忙 赞 :+1:
      • pdog18:通过 typeadapter ,我应该可以实现返回数据部分字段解密后生产的泛型对象的问题,谢谢作者,很有帮助
      • 你好_ddb0:看完后突然感觉自己不会用json了:joy: 之前只是简单的用来解析json数据,这序列化和反序列化没接触过。好文,再细细看多几遍。
      • HelloZZZZ:你好,可以请教一下吗。
        正确的时候返回的是{ "result":0,"msg":"密码错误","data":{ } },
        错误的时候返回的是{ "result":0,"msg":"密码错误","data":[ ] },
        按你上面那个方法registerTypeHierarchyAdapter了但是,这个方法只走一次,就是最外层会走这个方法,里面解析data时它就不走这个方法了。
      • woniu0936:请教一个问题,当api返回的数据没有规律的时候,怎么封装比较好
        woniu0936:就是说api返回的数据结构不统一,不固定
      • 放开那芒果:先谢谢作者,然后感觉我只能看懂第一第二篇,第三第四篇码代码感觉自己码不出来,很是迷茫:sweat:
        SMSM:Debug模式 一行一行的走代码 走三遍自己就全理解了
      • 准备流浪的雪:当服务器返回的body是空的时候怎么处理?报错: at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1393)
      • iDragonfly:4篇都看了,写得很好:+1:
      • jmpfelicity:结合实际场景 写的不错 期待后续文章
      • 小范屯:还有怎么兼容普通java bean 为null的情况,例如{"person":null}
        小范屯:我知道了,就没法兼容(我不想返回null,而是一个new Person())
        /** @Override public T read(JsonReader in) throws IOException {
        if (deserializer == null) {
        return delegate().read(in);
        }
        JsonElement value = Streams.parse(in);
        if (value.isJsonNull()) {
        return null;
        }
        return deserializer.deserialize(value, typeToken.getType(), context);
        }**/
        怪盗kidou:@小范屯 详细描述一下,没有太懂你的遇到问题是啥
      • 小范屯:在处理兼容问题的方案一下, {"list":null}这种是不行的。请问楼主没有其他的解决办法(gson 2.8)
        SMSM:

        public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
        @Override
        public StringBuilder read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
        }
        return new StringBuilder(in.nextString());
        }
      • 彩色的夜晚:内容很全,谢谢作者.

        帮忙了. :blush:
      • Wavky:冒昧请教一个问题:

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

        这部分能详细说明一下吗?官方文档貌似没有提及这一点。
        失效的问题对于JsonSerializer与JsonDeserializer也一样吗?
        失效的范围是针对GsonBuilder的其他所有设置?
        SMSM:@Wavky 是的
        Wavky:@怪盗kidou 也就是说,优先级是 registerTypeAdapter() > 注解 > 内置TypeAdapter,而registerTypeAdapter中依据注册先后顺序判断TypeAdapter、JsonSerializer、JsonDeserializer之类的优先顺序,是这样理解吗?
        而针对自定义类型,使用TypeAdapter、JsonSerializer进行序列化时应该都是完全手工接管整个序列化过程,所以setDateFormat、Since、Expos之类的设定都没有任何意义,是这个意思?
        怪盗kidou:@Wavky 也不是,JsonSerializer、JsonDeserializer、TypeAdapter、TypeAdapterFactory都是按GsonBuilder的注册顺序来的,并且GsonBuilder注册的优先级是最高的,所以即使用注解指定了TypeAdapter或TypeAdapterFactory,优先级都是比较低的,只比Gson内置的枚举和反射用的TypeAdapter高而已
      • HenryChao:感谢博主的总结。有一个疑问:官网上说,Gson默认不能序列化(发序列化)成员内部类,静态嵌套类是可以的。可是,我这边写Demo发现,成员内部类也是可以直接序列化的。贴下我的Demo:
        // inner class
        public class A {
        public String a = "a";

        class B {
        public String b = "b";
        public B() {
        }
        }
        }

        // test
        public static void serialize() {
        Gson gson = new Gson();
        gson.toJson(new A().new B(), System.out); // {"b":"b"}
        }

        public static void deserialize() {
        Gson gson = new Gson();
        A.B deserialization = gson.fromJson("{\"b\":\"b\"}", A.B.class);
        System.out.println("deserialization= " + deserialization);
        }
        HenryChao:Android上试了一下,依赖Gson2.7的库,测试依然可行.这么重要的类库,文档不至于出错吧..我看了下git,文档也是3个月前更新到了2.7的版本.可能还是自己理解偏了.
        怪盗kidou:@HenryChao 可能是这部分文档太旧了,我试了一下从1.7.1开始,就 上面给的例子而言是可以的,好像用到了sun的私有API,所以可能在Android上就行不通了,还没有去测试。
        HenryChao:@HenryChao 我又试了下,确实能够反序列化成功,不过此时打印内部类的外部类引用,结果是null.
      • 你家鹏大大:你好,我使用Retrofit + Gson + okhttp3.0 已经定义好了对应的Model。但是现在要做数据校验。需要将服务器端的JSON字符串做一个MD5。但是我查询了Retrofit的方法,response中没有办法再次获取到JSON字符串了(我已经转化成了确定的Model了)。我又想到了GSON能转换成JSON字符串,但是转换后的顺序和服务器发来的顺序不同。造成了MD5不相同。怎么办、、、555、、实在不行,我就要将Retrofit所有的返回全部替换成String返回,然后通过Gson解析了。但是有好多个接口啊。。。晕。求指导。
        怪盗kidou:@你家鹏大大 :grin:
        你家鹏大大:@怪盗kidou 是的,最后还是通过okhttp的拦截器做一个校验。能解决。到公司再测试一下~谢谢你的回答~。这种方式,不要改接口的任何代码就能生效。真是书到用时方恨少啊。要是不遇到这个需求,可能都不会去了解下okhttp的拦截器。太好用了。555.用拦截器应该是比较方便的解决方案了。
        怪盗kidou:@你家鹏大大 可以在okhttp上加一个拦截器作对比,不过还是有点麻烦,其它暂时没有想到其它什么更好的方法,但md5是否真的有这个必要呢?
      • hewking:遇到一个问题,就是当 解析json数据得到的 java 对象,如果数据格式不正确,会有字段 为 null
        但是解析过程中不会报错,有没有办法,让报错,或者 有为null 的自动过滤掉,不显示到返回得到的 list中?
        SMSM:1、不报错还不好啊。不报错可以,提高了系统的稳定性。在你使用的时候自行进行判空。
        2、让报错实现方式也简单。比如自行遍历java属性列表进行判空,有空时,抛出异常。
        3、『不显示到返回得到的 list中』没有太明白
      • Neogx:感谢楼主的付出····看了这个系列后,基本知道GSON怎么用了··以后有什么问题 都可以解决!感谢感谢
      • Jafir:还有个问题就是,看了第一篇里面,一般如果数据类型是 自定义类型,你用的是typeToken 就可以解决,然后可以解析了。 请教依稀 typeToken 和 这里的typeAdapter 他们的使用场景有什么不同呢?他们的区别在哪里?
        SMSM:在反序列化List<User>时, 为解决泛型擦除引入了TypeToken,并通过ReflectiveTypeAdapterFactory反射的方式 实例化User对象。如果想要提高反射效率,就是不使用反射,而是自己定义TypeAdapter,并new的方式实例化后,并设置属性值
        怪盗kidou: @Jafir TypeToken只是用来取Type的一个帮助类,使用哪个TypeAdapter是由Type来决定的
      • Jafir:博主,看了第六条,说的是 不为空就返回list集合 为空就返回 String字符串。
        你的2种解决方案我没看太懂,方法里面的json 指的是 返回的json么?还是只指的返回的json里面的 data 的json?
        if else if是返回有数据的list集合 而 else是返回 空集合,不是说为空是返回String字符串么?
        怪盗kidou:@Jafir 字符串没办法赋值给List啊,所以才返回空List
        Jafir:@怪盗kidou 我是说的 那两种 没看懂。 方法参数的 json指的是什么? if else返回的结果,应该是 有数据返回数据集合 没数据返回 String字符串的嘛 怎么变成了 空集合,而且返回参数类型也规定的是 List
        怪盗kidou: @Jafir 和第一种方案是一模一样的,只是不用重新创建一个新的gson,而是利用现有的gson,那也不用重新配置了
      • 键盘男:怪盗兄,如果有时间,能不能写一下 JsonReader?那个资料好少,写typeAdapter的时候,有些错误很难搞
        怪盗kidou:@键盘男kkmike999 不会啊,String是以引号开头的
        键盘男:@怪盗kidou 感觉jsonReader比较坑,如果是'{'开头,就认为是jsonObject,那我的String真的是'{'开头就悲剧了……
        怪盗kidou:@键盘男kkmike999 就使用而言很简单啊
        beginObject: {
        endObject: }
        beginArray:[
        endArray:]
        hasNext:对象或数组中还没有其它元素
        nextName:在对象中取出下一个json的键
        nextString、nextInt、nextXXX:从json中取出值,并转换成相应的类型
        skipValue:跳过取值,如果这个键你用不上,那么值也用不上,所以跳过,不然你用next的时候会有问题

        其它的方法都是反射时使用比较多了,就这么几个方法,你尝试着用它手动解析一下,如果你试过之后觉得还有必要,那个时候我再写不迟。 :smile:
      • 娃娃要从孩子抓起:期待泛型、反射、注解和HTTP 内容的文章。博主加油
      • GoodGoodStuday:解决了,没采用这种办法,,判断是不是空值,,实现的方法很low,,哈哈哈
      • GoodGoodStuday:大哥,能在请教请教你不。。。你的方案二,怎么修改呢,云里雾里的。。
      • GoodGoodStuday:大哥,我的是这样的, 如果数据时,data返回对象的形式,, 没有数据时,返回这个[]。。 上面第二个实例方案,不是很明白。。
        HelloZZZZ:你好,可以请问一下你是怎么解决的吗
      • Y0Y:期待你的新文章,同时请教个问题:服务器的json
        {code:
        msg:
        data:{}
        }
        pojo中data用的泛型,现在gson解析泛型会了,但是在retrofit2.0中该怎么添加convertor呢?
        Y0Y:刚试了一下,原来是我想多了...谢谢
        Y0Y:这连泛型都会自动解析么?
        怪盗kidou:@Y0Y Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .url(BASE_URL)
        .build();
      • Jaesoon:好好
      • 81ad24cedc67:收获很大, 非常感谢. 同时其他更多精彩的文章 :monkey_face:
      • 小小流氓:学习了,收藏了?
      • 3b3b0abb0d3b:学到不少,期待
      • 2403dae8b2e4:大神,问个问题,json字符串有一个avatar头像字段,值为/avatar/default/7.jpg,没有地址前缀,我想在解析的时候加上前缀。用Gson能做么?
        Gson好像不会调用set,我本打算在set里面设置,可是这样没效果。代码如下
        public void setAvatar(String avatar) {
        this.avatar = "http://www.baidu.com&quot; + avatar;
        }
        怪盗kidou:@sonuan 这个你不能只针对某个字段,你一处理就得处理该对象的其它字段,还有就是你可以反序列化之后,统一处理也行啊,要么就优化一下getAvatar方法,只在第一次拼接
        2403dae8b2e4:@怪盗kidou 在listview里,放在get的话,就不能放太繁杂的拼接,不然会卡顿,想放在set里面完成。我再看看其他的解析框架。3q。
        怪盗kidou:@sonuan set里不行的话,你可以在get里做啊.
        public String getAvatar(){
        if(!avatar.startWith("/")){ //或者判断是不是http开头
        return "http://www.baidu.com&quot;+avatar;
        }
        return avatar;
        }
        其它方法当然也行,只是能用这种方式解决就没必要用Gson来解决
      • Diffey:有一个问题:@JsonAdapter注解使用JsonSerializer报错 java.lang.IllegalArgumentException: @JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference 我的Gson是最新版2.6.2
        怪盗kidou:@Diffey Sorry,我的错,@JsonAdapter注解 确定只支持 TypeAdapter 和 TypeAdapterFactory,@JsonSerializer 和@JsonDeserializer 只支持和GsonBuilder.registerTypeAdapter或GsonBuilder.registerTypeHierarchyAdapter 配合,应该是三者都可以和 GsonBuilder.registerTypeAdapter或GsonBuilder.registerTypeHierarchyAdapter 配合。
        Diffey:@怪盗kidou 关于@JsonAdapter注解,只接受TypeAdapter or TypeAdapterFactory类型,文中说的JsonSerializer或JsonDeserializer并不可以。我看了一下2.4的源码,也是如此。请问,@JsonAdapter的值是JsonSerializer或JsonDeserializer运行测试过么?
        怪盗kidou: @Diffey 给你提示的是必需是TypeAdaper或者TypeAdaprtFactory,我不知道你给的值是啥,可能2.6.2只支持这两种,我用的2.4
      • SimonJ:有个需求一直不知道如何优雅的处理,

        某些 REST API 返回数组列表的时候,

        会多加一层 META DATA,

        例如
        {
        "meta":{
        "page":0,
        "total":10
        },

        "data":{
        .....
        }

        }

        有时候不需要知道 meta data 的 info,

        所以不想多写一个 class 出来,

        有除了 TypeAdapter 以外的做法来处理这种情形吗?
        怪盗kidou:@SimonJ 只要有一个地方用到了你就得meta,必须得有个字段来存储,如果你没有meta的接口,反正也是null
        SimonJ:谢回覆,

        我明白可以使用泛型,

        但是这样就要在多一个类,

        有些专案实际上我是用不到 meta 的 资料的,

        实在不想多一个 class,

        所以除了这个方法以外,

        没有其他方法了?
        怪盗kidou:@黃朝揚 META 里的信息不会太多,可能很多个API用的META都是重复的,你把把定们合成一个类就行了,这里是"page"和"total",另一个是“XX”和“YY”那你的可以定义为
        public class Meta{
        public int page;
        public int total;
        public String XX;
        public String YY;
        }
        如果每个接口都不一样,那你也可以像data一样用泛型啊,根本用不着用TypeAdapter
        public class Result<M,T> {
        public int code;
        public String message;
        public M meta;
        public T data;
        }
      • 键盘男:怪盗哥好厉害啊ヾ(◍ ° ㉨ ° ◍)ノ゙ 晚上试试空字符串~
      • 文盲:楼主高瞻远瞩
      • 9a0a59e34476:期待kidou哥的新作 :grin:
        怪盗kidou: @杨牧远 敬请期待😂

      本文标题:你真的会用Gson吗?Gson使用指南(四)

      本文链接:https://www.haomeiwen.com/subject/molulttx.html