美文网首页AndroidWorldRx、Retrofit、Gson、MVP、Dagger2JAVA
你真的会用Gson吗?Gson使用指南(一)

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

作者: 怪盗kidou | 来源:发表于2016-03-30 22:24 被阅读210407次

本文为作者根据日常使用结合Gson源码注释及wiki所作的原创内容,转载请注明出处。
本文链接:http://www.jianshu.com/p/e740196225a4

JSON (官网) 是一种文本形式的数据交换格式,它比XML更轻量、比二进制容易阅读和编写,调式也更加方便。其重要性不言而喻。解析和生成的方式很多,Java中最常用的类库有:JSON-Java、Gson、Jackson、FastJson等。

该系列其它文章

注:此系列基于Gson 2.4。

对Gson使用很自信的大大可以点击关闭啦。

本篇文章的主要内容:

  • Gson的基本用法
  • 属性重命名 @SerializedName 注解的使用
  • Gson中使用泛型

一、Gson的基本用法

Gson提供了fromJson()toJson() 两个直接用于解析和生成的方法,前者实现反序列化,后者实现了序列化。同时每个方法都提供了重载方法,我常用的总共有5个。

基本数据类型的解析

Gson gson = new Gson();
int i = gson.fromJson("100", int.class);              //100
double d = gson.fromJson("\"99.99\"", double.class);  //99.99
boolean b = gson.fromJson("true", boolean.class);     // true
String str = gson.fromJson("String", String.class);   // String

注:不知道你是否注意到了第2、3行有什么不一样没

基本数据类型的生成

Gson gson = new Gson();
String jsonNumber = gson.toJson(100);       // 100
String jsonBoolean = gson.toJson(false);    // false
String jsonString = gson.toJson("String"); //"String"

POJO类的生成与解析

public class User {
    //省略其它
    public String name;
    public int age;
    public String emailAddress;
}

生成JSON:

Gson gson = new Gson();
User user = new User("怪盗kidou",24);
String jsonObject = gson.toJson(user); // {"name":"怪盗kidou","age":24}

解析JSON:

Gson gson = new Gson();
String jsonString = "{\"name\":\"怪盗kidou\",\"age\":24}";
User user = gson.fromJson(jsonString, User.class);

二、属性重命名 @SerializedName 注解的使用

从上面POJO的生成与解析可以看出json的字段和值是的名称和类型是一一对应的,但也有一定容错机制(如第一个例子第3行将字符串的99.99转成double型,你可别告诉我都是字符串啊),但有时候也会出现一些不和谐的情况,如:
期望的json格式

{"name":"怪盗kidou","age":24,"emailAddress":"ikidou@example.com"}

实际

{"name":"怪盗kidou","age":24,"email_address":"ikidou@example.com"}

这对于使用PHP作为后台开发语言时很常见的情况,php和js在命名时一般采用下划线风格,而Java中一般采用的驼峰法,让后台的哥们改吧 前端和后台都不爽,但要自己使用下划线风格时我会感到不适应,怎么办?难到没有两全齐美的方法么?

我们知道Gson在序列化和反序列化时需要使用反射,说到反射就不得不想到注解,一般各类库都将注解放到annotations包下,打开源码在com.google.gson包下果然有一个annotations,里面有一个SerializedName的注解类,这应该就是我们要找的。

那么对于json中email_address这个属性对应POJO的属性则变成:

@SerializedName("email_address")
public String emailAddress;

这样的话,很好的保留了前端、后台、Android/java各自的命名习惯。

你以为这样就完了么?

如果接中设计不严谨或者其它地方可以重用该类,其它字段都一样,就emailAddress 字段不一样,比如有下面三种情况那怎么?重新写一个?

{"name":"怪盗kidou","age":24,"emailAddress":"ikidou@example.com"}
{"name":"怪盗kidou","age":24,"email_address":"ikidou@example.com"}
{"name":"怪盗kidou","age":24,"email":"ikidou@example.com"}

为POJO字段提供备选属性名
SerializedName注解提供了两个属性,上面用到了其中一个,别外还有一个属性alternate,接收一个String数组。
注:alternate需要2.4版本

@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;

当上面的三个属性(email_address、email、emailAddress)都中出现任意一个时均可以得到正确的结果。
注:当多种情况同时出时,以最后一个出现的值为准。

Gson gson = new Gson();
String json = "{\"name\":\"怪盗kidou\",\"age\":24,\"emailAddress\":\"ikidou_1@example.com\",\"email\":\"ikidou_2@example.com\",\"email_address\":\"ikidou_3@example.com\"}";
User user = gson.fromJson(json, User.class);
System.out.println(user.emailAddress); // ikidou_3@example.com

三、Gson中使用泛型

上面了解的JSON中的Number、boolean、Object和String,现在说一下Array。

例:JSON字符串数组

["Android","Java","PHP"]

当我们要通过Gson解析这个json时,一般有两种方式:使用数组,使用List。而List对于增删都是比较方便的,所以实际使用是还是List比较多。

数组比较简单

Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);

但对于List将上面的代码中的 String[].class 直接改为 List<String>.class 是行不通的。对于Java来说List<String>List<User> 这俩个的字节码文件只一个那就是List.class,这是Java泛型使用时要注意的问题 泛型擦除

为了解决的上面的问题,Gson为我们提供了TypeToken来实现对泛型的支持,所以当我们希望使用将以上的数据解析为List<String>时需要这样写。

Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());

注:TypeToken的构造方法是protected修饰的,所以上面才会写成new TypeToken<List<String>>() {}.getType() 而不是 new TypeToken<List<String>>().getType()

泛型解析对接口POJO的设计影响
泛型的引入可以减少无关的代码,如我现在所在公司接口返回的数据分为两类:

{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}

我们真正需要的data所包含的数据,而code只使用一次,message则几乎不用。如果Gson不支持泛型或不知道Gson支持泛型的同学一定会这么定义POJO。

public class UserResponse {
    public int code;
    public String message;
    public User data;
}

当其它接口的时候又重新定义一个XXResponsedata的类型改成XX,很明显code,和message被重复定义了多次,通过泛型的话我们可以将codemessage字段抽取到一个Result的类中,这样我们只需要编写data字段所对应的POJO即可,更专注于我们的业务逻辑。如:

public class Result<T> {
    public int code;
    public String message;
    public T data;
}

那么对于data字段是User时则可以写为 Result<User> ,当是个列表的时候为 Result<List<User>>,其它同理。

PS:嫌每次 new TypeToken<Result<XXX>new TypeToken<Result<List<XXX>> 太麻烦, 想进一步封装? 查看我的另一篇博客:** 《搞定Gson泛型封装》 **

结语

本文主要通过代码向各位读者讲解了Gson的基本用法,以后还会更新更多更高级的用法,如果你还不熟悉 注解泛型 那么你要多多努力啦。

如果你有其它的想了解的内容(不限于Gson)请给我留言评论,水平有限,欢迎拍砖。


4月6日补充
有说看不懂Result那段怎么个简化法,下面给个两个完整的例子,User和List<User> 。

没有引入泛型之前时写法:

public class UserResult {
    public int code;
    public String message;
    public User data;
}
//=========
public class UserListResult {
    public int code;
    public String message;
    public List<User> data;
}
//=========
String json = "{..........}";
Gson gson = new Gson();
UserResult userResult = gson.fromJson(json,UserResult.class);
User user = userResult.data;

UserListResult userListResult = gson.fromJson(json,UserListResult.class);
List<User> users = userListResult.data;

上面有两个类UserResultUserListResult,有两个字段重复,一两个接口就算了,如果有上百个怎么办?不得累死?所以引入泛型。

//不再重复定义Result类
Type userType = new TypeToken<Result<User>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
User user = userResult.data;

Type userListType = new TypeToken<Result<List<User>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType);
List<User> users = userListResult.data;

看出区别了么?引入了泛型之后虽然要多写一句话用于获取泛型信息,但是返回值类型很直观,也少定义了很多无关类。

相关文章

网友评论

  • 涛桑_:哇哦, 写得真棒!
  • 想回老家了:我就想知道怎么解析如下json

    {
    0={
    img=0.0,
    name=非会员,
    qualify_amount=0.0
    },
    1={
    img=1.0,
    name=一级会员,
    qualify_amount=100.0
    },
    2={
    img=2.0,
    name=二级会员,
    qualify_amount=300.0
    },
    3={
    img=3.0,
    name=三级会员,
    qualify_amount=500.0
    想回老家了:@怪盗kidou 已解决,确实是这样的。数据结构还是要学习啊😂
    怪盗kidou:@刘喆 Map<Integer,XXX>
  • f85866e9809c:写的通俗易懂,劳资反手就是一个赞
  • 熊猫的脑壳:可以 对我来说 还是有点收获的
  • 我素熊猫::sob: 小姐姐这么厉害才24吗
    我素熊猫:@怪盗kidou :stuck_out_tongue_closed_eyes: 也很强 向你学习哈哈
    怪盗kidou:我不是小姐姐:joy: ,16年的时候24
  • 全球顶尖伪极客:搜索了半天翻到别的文章,打算自己做点笔记写点博客,后面翻到作者的文章,直接放弃挣扎了,笔记就是作者的链接。
    怪盗kidou:@haker 哈哈,目前我的这篇博客在Google上是排第一的
  • a117173647c2:支持楼主系列文章,每看一遍都是收获!非常感谢
  • 杨晓是大V:写的很好,已经第四次路过
    怪盗kidou:@virusbam 😀
  • 40f614d9bb95:fromJson用来解析的,toJson用来生成的!
  • 222462eaf67e:将code等公用字段写入基类,然后创建的各个数据类继承基类就可以了
  • 可乐_JS:棒棒哒
  • Nisus_Liu:您好, 请问除了注解还有其他办法吗?
    1. 当原始的类源码不能修改.
    2. 或者有些需要, 有些不需要, 我想要灵活设置, 有办法吗?
    Nisus_Liu:@怪盗kidou 好的, 谢谢回复!
    怪盗kidou:可以看后续的文章,里面有讲用typeAdapter
  • 10facb87c2b6:超棒的,解决了我的疑惑,不懂泛型和注解的我需要多多努力咯:confounded:
  • 69a084500851:赞👍
  • 89d1339f8a31:楼主这篇文章写得很好! 但是有个地方还是不太明白.
    为什么TypeToken的构造方法是protected修饰的,就需要写成new TypeToken<List<String>>() {}.getType() 而不是 new TypeToken<List<String>>().getType()呢? 希望能得到一些提示~
    89d1339f8a31:@怪盗kidou 菩提灌顶! 多谢楼主
    怪盗kidou:@____Zero 是因为 new TypeToken<List<String>>() 里无法保存泛型信息(泛型擦除),所以必须让你写一个子类,在子类里来记录这个泛型信息,而 protected 你不就没法直接new TypeToken()了么,所以这时候你就必须要使用子类了,而直接在后面加 {} 就是写一子类最快的办法
  • luckbing:赞,学到了谢啦大神!:relaxed:
  • 陈文超happylion:这个是最简单的用法,如果讲下typetoken怎么从字节码中获取的泛型。讲一下gsonAdapter 更好一些。
    陈文超happylion:@怪盗kidou 赞
    怪盗kidou:@陈文超happylion 后面还有好几篇呢,有讲
  • 烈酱:说得好,我选择砍死不合作的后台
  • 你好_平凡: public static <T> BackResult<List<T>> jsonToListObject(String jsonObject,Class<T> clas) {
    //得到list<t>
    Type listType = new ParameterizedTypeImpl(List.class, new Class[]{clas});
    //得到backresult<list<T>>
    Type type = new ParameterizedTypeImpl(BackResult.class, new Type[]{listType});

    BackResult<List<T>> backResult = new Gson().fromJson(jsonObject, type);

    return backResult;
    }
    请教楼主, java.lang.IllegalArgumentException: Expected a Class, ParameterizedType, or GenericArrayType, but <com.github.darren.smilemo.bean.ParameterizedTypeImpl@90213f6> is of type com.github.darren.smilemo.bean.ParameterizedTypeImpl
    说我传的类型不正确,这个是什么情况额
  • a18c975d7fc1:写的挺不错的,已经收藏了。

    源码解析:http://suo.im/1FKU2k


    e717d49b5f9f:写的蛮用心的,希望多多坚持那
  • 望舒晨:大神,求QQ
    怪盗kidou:不是什么大神,你要QQ干啥,是有啥问题么,有问题就直接评论,我会回的,也可以给遇到同样问题一些帮助
  • 请叫我财迷:大神有兴趣写一些关于自定义动画,自定义控件这些么。磕瓜坐等~:relaxed:
  • 请叫我财迷:赞赞赞~~~
  • luckbing:学习了,谢谢大神
  • 8f8ab6529ed5:楼主写的真心不错, 赞一个.
  • 柴柴777:很详细
  • 自由快挂:gson.toJson("><") -> "\u003e\u003c"

    想要结果是 "><" 要怎么处理 ?
    自由快挂:Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .disableHtmlEscaping()
    .create();
  • Jaycee88:已收藏,已关注!
  • Jackson_ba56:写得很用心,谢谢你的分享
  • fbdcef9a8b33:http://stackoverflow.com/questions/14713736/gson-parse-json-with-array-with-different-object-types
    http://baobab.kaiyanapp.com/api/v4/tabs/selected
    咨询下像上面这种的多类型集合json解析,有没有更简便的方法,retrofit该怎么处理
  • b1aee88289b2:言简意赅,赞!阅读公司之前的项目代码的时候,遇到Gson,网上搜了一下,这篇文章讲的最好,言简意赅,很实用。
    怪盗kidou:@b1aee88289b2 :grin:
  • 有范管理美学:楼主要是再写一个Fastjson系列的那就好了!
    怪盗kidou:@公子沛 了解了,如果以后有机会的话可以写一下
  • 298ac7ee7f62:文章开头提到的99.99是什么意思?第二行和第三行代码具体区别?仅仅是容错吗
  • 3a77ab3a60d0:gson效率慢得一逼 推荐logansquar
    lc_b4b5:gson 是官方的 bug相对少 慢点就慢点吧 我觉得还挺快 遇到有bug 的框架 那就坑了
    luckbing:@公子沛 fastjson是不是有个坑,就是POJO必须有个无参构造器,妈蛋,浪费我好长时间找这个bug,到解析直接闪退,错误日志都没得一个
    有范管理美学: @ICU4C fastjson性能好,公司在用
  • Mr韶先生://不再重复定义Result类
    Type userType = new TypeToken<Result<User>>(){}.getType();
    Result<User> userResult = gson.fromJson(json,userType);
    User user = userResult.data;
    只用 userType 就可以获取除了User的信息吗?比如说 code和message?
    怪盗kidou:@Mr韶先生 Result中本来就有code和message,这可以反射出来,但由于泛型的存在,gson并不知道data的类型,但从userType可以得到data的类型
    Mr韶先生:@怪盗kidou 不是不练习而是不理解为什么可以这样?
    怪盗kidou:@Mr韶先生 试试啊,都不练的么
  • 放开那芒果:跟着《你真的会用Gson吗?Gson使用指南(一)》巧了一遍代码,遇到个问题,(希望有朋友能帮指点指点,先谢谢了~)
    UserListResult userListResult = gson.fromJson(json,
    UserListResult.class);
    List<User> userList = userListResult.getData();
    for (int i = 0; i < userList.size(); i++) {
    Log.v("TAG", "=============>>" + userList.get(i).getName() + ":"
    + userList.get(i).getAge() + ":"
    + userList.get(i).getEmailAddress());
    }

    或者
    Type userListType = new TypeToken<Result<List<User>>>() {
    }.getType();

    Result<List<User>> userListRes = gson.fromJson(json, userListType);
    List<User> userList = userListRes.getData();
    for (int i = 0; i < userList.size(); i++) {
    Log.v("TAG", "=============>>" + userList.get(i).getName() + ":"
    + userList.get(i).getAge() + ":"
    + userList.get(i).getEmailAddress());
    }
    这样子写都会报错,错在获取userListType上,03-03 16:35:19.005: E/AndroidRuntime(4792): Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 10 path $.data
    另外的两种方法都可以获取到数据,找了一天了,不知问题所在。(为什么评论不能放截图呢~呜呜~)
    放开那芒果:@怪盗kidou 原来要分两种JSON格式的,是我愚钝了:

    String json = "{\"data\":{\"name\":\"王大锤\","
    + "\"email_address\":\"bbbbb\",\"age\":\"24\"},"
    + "\"code\":\"12345\",\"message\":\"同学你好!\"}";

    String json = "{\"data\":[{\"name\":\"王大锤\","
    + "\"emailAddress\":\"bbbbb\",\"age\":\"24\"}],"
    + "\"code\":\"12345\",\"message\":\"同学你好!\"}";
    放开那芒果:@怪盗kidou Type userType = new TypeToken<Result<User>>(){}.getType();
    Result<User> userResult = gson.fromJson(json,userType);
    User user = userResult.data;但是我这样写是没问题的,是可以获取到值de
    怪盗kidou:@放开那芒果 你用的JSON有问题,和Entity的类型不匹配
  • JackCook:不错
  • LuciferZ:大神,请教一个问题。公司的后台接口返回的data字段,有的是data:{…},有的是data:{[…]},这两种方式里,实力对应的实体对象有可能是一样的,比如说data:{User}和data:{[User, User,…]}。想用retrofit作为网络框架,我参考官方例子貌似只能做出两个实体,一个User,另外一个是包了一个List<User>实体。这样做有点太不好了,一定有什么别的办法我没找到。想请教大神一下:smile:
    怪盗kidou:@LuciferZ 不知道是理解错了还是只啥的,怎么感觉这结构有点问题啊
    LuciferZ:@怪盗kidou 恩,已经这么做了,就是感觉另外的实体里面只包了个List有点别扭...期待大神的新文章:grin:
    怪盗kidou:@LuciferZ 也没有说只能是他俩,里面是啥是根据你的json结构来的嘛,把最外层抽成另一个实体
  • 259c1c37b7cc:谢谢楼主
  • 李梦成:不应该直接用List<User>吗?
    怪盗kidou:@而不自知 上下文呢
  • 4b760e12eaf8:有个问题请教下,泛型擦除那块的。请问List<String>.class改为List.class不是可以成功转的吗
    怪盗kidou:@iceblock 是可以转成功,如果里面也只有String的话,结果里也是正常的,但是,你后面使用的时候不就更麻烦了么,泛型不就是为了解决这样的问题么。
  • 团子吃蛋挞:想问一个问题…
    result的data是泛型T,new TypeToken<result<List<T>>的时候就会变成LinkedTreeMap。
    那么针对result返回值是否list还要作进一步判断,从而调用不同的方法来获取type,
    如果我想在接收部分统一处理返回值,请问怎么通过同一种途径获取这个type呢?
    怪盗kidou:@团子吃蛋挞 对于这个接口希望的数据类型不应该是预先知道的么,就好像你知道这个接口会返回json格式的数据而不是xml一样
    团子吃蛋挞:@怪盗kidou 看了你的那篇文章,如果typetoken<List<T>>的话
    ```java
    Type type = TypeBuilder
    .newInstance(Result.class)
    .beginSubType(List.class)
    .addTypeParam(clazz)
    .endSubType()
    .build();
    ```
    如果不是list,则是
    ```java
    Type type = TypeBuilder
    .newInstance(Result.class)
    .addTypeParam(clazz)
    .build();
    ```

    list比非list多了一步处理,那么在未确定T的类型之前,则我的返回值是这样的
    ```
    public class Result<T>{
    int code;
    T message;
    }
    ```

    在网络请求的返回部分这样处理
    ```java
    Type type = new TypeToken<T>(){}.getType();
    Result result = new Gson().fromJson(jsonStr, type);
    ```
    那么问题来了,我只有一个jsonStr(则服务器返回值),我并不知道里面包含的内容中message是list还是只是一个json object,如果要以你文章的那种处理方式,我得先知道是否list,然后再分别处理,那我这里请问该怎么做呢?
    怪盗kidou:在new TypeToken时,所有的类型都是一有确定的值,如 new TypeToken<result<List<User>>,这个时候 你可以把T的值当作是User,当你还是用的T,那么Gson还是不知道你要把 T 当成什么类型,可以看看我的别一篇文章,Gson范型封装
  • 37254eb7a723:学到了,谢谢。
    37254eb7a723:@怪盗kidou 文章很好,如果可以介绍使用场景我觉得会更好:smile:
    怪盗kidou:@intozhou :+1:
  • 9bad1df6b369:toJson方法,把包含Base64的对象转为String时,这个Base64的后面有换行符怎么去掉啊?
    比如这样:{
    "client_token": "AjD0BlcUEQMMJeN32G24VFRMvW7lRmlWkZSsuXUfl6xk",
    "client_type": "android",
    "current_version": "1.0.0",
    "signature": "MTg0NTU3NzU4ODg6Z3hjY2ZmZmZmZmY=\n"
    }
    这个signature字段的值 末尾的换行符
    怪盗kidou:@习惯宠着你 :smile:
    9bad1df6b369:@怪盗kidou 厉害了world哥,搞定
    怪盗kidou:signature是个Base64对象?这个值本身不是String 类型么?如果你用的是Android自带的Base64,那在生成时加一个 Base64.NO_WRAP 的flag,就不会有换行符了
  • zeor2016:很受用
  • FosterDylan:看评论也学习不少。
  • sunnyaxin:楼主,属性重命名那里,找到注解类了,可是好像文件是锁住的不能修改啊,肿么办?
  • 热血沸腾:不错,有个问题,普通Json解析有 optXXX 和 getXXX
    如果使用get 当解析错误时会崩溃,opt则会打印错误log直接return
    那么Gson会怎么处理呢? 我关心的是,如果我的项目换成gson,那么如果避免未捕获的异常出来时候,程序不会崩溃。 谢谢
    热血沸腾:@怪盗kidou 收到,谢谢
    怪盗kidou: @热血沸腾 一句话总结就是:只需要保证json的 格式 符合预期就就行,至于少字段多字段都无所谓,但只要格式不对,或者类型与bean不匹配都会抛异常
  • 0400abbf637a:请问下面这种情况怎么处理?
    正常情况返回是这样
    {
    "status": 1,
    "message": "操作成功",
    "data": {
    "id": 6,
    "username": "测试"
    }
    }
    public class User {

    public int status;
    public String message;
    public Info data;

    public class Info {
    public int id;
    public String username;
    }
    }
    解析User user = new Gson().fromJson(result, User.class);
    这样是没有问题的,不过可能请求参数出错,服务端返回的是
    {
    "status": 0,
    "message": "缺少参数",
    "data": ""
    }
    这样解析就出错,这种情况要怎么处理?
    知乎提问链接https://www.zhihu.com/question/53434568?guide=1
    0400abbf637a:@怪盗kidou 或者在回调Callback判断再解析,这样处理也不好
    0400abbf637a:@怪盗kidou 第四篇是序例化才起作用的吧,不序例化就不行了。
    我用的是retrofit,在自定义的Converter里这样处理
    if (!TextUtils.isEmpty(result) && result.contains("\"data\":\"\"")) {
    result = result.replace("\"data\":\"\"", "\"data\":{}");
    }
    然后再解析,感觉超不好。想让php改,口水说干都不肯改。。。
    怪盗kidou:@无戈 后台一定是PHP写的 :joy: ,最好的办法是让后台不返回 data,或data为null,反正这个时候data也没什么用,当然也可以用JsonDeserializer来解决(见第四篇),但不是什么好注意,该是谁的问题就谁解决去
  • stefanJi:决定从android的原生json解析转到Gson了
    swensun:原生json太难用了。
  • 妙法莲花1234:@SerializedName 真心神器,感谢
    妙法莲花1234: @柴柴777 蓦然回首😁
    柴柴777:哎呦
  • Ning1994:很详细的入门指南,谢啦
  • Waizau:比较好奇,无论是gson还是fastjason这类库,使用的都是
    Type superClass = getClass().getGenericSuperclass();
    Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    效率如何? :smiley:
  • _上邪_:大神的写的好详细,好细致啊,有一种这文章是女生写的一样,很细腻。
    看到一半的时候,拉到底点了喜欢进行收藏文章,
    评论看到一半的时候,拉倒顶部点了订阅作者,希望出更多的东西,多了解一些工具类,对编码效率很有帮助


  • 薄炳鑫:作者总结的不错
    怪盗kidou:@薄炳鑫 :grin:
  • smallpi:发现楼主大人写了很多android干货啊,必须关注 :kissing_closed_eyes:
    怪盗kidou:@smallpi :grin: 感谢关注
  • 5a6eddffcac1:想把
    Type userType = new TypeToken<Result<User>>(){}.getType();
    Result<User> userResult = gson.fromJson(json,userType);
    User user = userResult.data;

    Type userListType = new TypeToken<Result<List<User>>>(){}.getType();
    Result<List<User>> userListResult = gson.fromJson(json,userListType);
    List<User> users = userListResult.data;
    封装成工具类,但是不知道怎么弄,自己弄了一个不知道是否是泛型的层级太多还是什么原因,一直数据类型异常,可以帮忙封装让我学习一下吗?
    怪盗kidou:@四夕一十 等我的新博客吧
    5a6eddffcac1:@四夕一十 对啊
    怪盗kidou:@四夕一十 你是想提供两个方法,分别针对Result<List<User>>和Result<User>,只需传User就行,而不用自己去生成Type?
  • 你家鹏大大:大神,我想问问。如果遇到,正确返回的时候。data是一个对象。错误的时候data是一个字符串的问题呢?
    正确:
    {
    "status": "200",
    "data": {
    "str": "123"
    }
    }
    失败:
    {
    "data": "错误参数!",
    "status": "1000"
    }
    遇到这样的情况。我使用的是Retrofit网络请求框架。目前所知的有一种解决方案,就是直接获取到JSON,解析成一个只有status的实体,然后判断是不是200。但是不优美。Gson应该有对应的解决方案。求解答!!!
    0400abbf637a:应该加一个message,错误提示放在message里,其它数据放在data里
    你家鹏大大: @怪盗kidou 哈哈。我想也是。要我们处理。我也是醉了。艰难。不过,上面一种方式还是能用的。
    怪盗kidou:@你家鹏大大 :joy: API设计不优美就不要怪GSON了
  • 5466dabd27c2:如果 我要解析 {abc:{“”,“”,“”}} 怎么样把 abc 的{“”,“”,“”}直接解析成String类型呢
    怪盗kidou:@5466dabd27c2 除非你手动解
    5466dabd27c2:@怪盗kidou 就是把一个json里面的json数据转化为String 解析出来
    怪盗kidou:@5466dabd27c2 :scream: ,这个我是没见过这么奇葩的
  • d8c693dd960c:写的很细致,感谢。
    怪盗kidou:@d8c693dd960c 希望对候有帮助 :grin:
  • Zack_zhou:"{\"name\":\"怪盗kidou\",\"age\":24,\"emailAddress\":\"ikidou_1@example.com\",\"email\":\"ikidou_2@example.com\",\"email_address\":\"ikidou_3@example.com\"}"


    为什么你的json字符串里会带有"\",以前都没有遇到过
    怪盗kidou:@Zack_zhou 在java中表示字符串就是用 “” 括起来的啊,那字符串中的"要用转义
  • 自己找知己:另外讨论个问题,我们的服务器返回的有的字段是空怎么办,解析时就会报错。如何做容错。
    比如服务器返回的Json是:{"name":,"age":24,"emailAddress":"ikidou@example.com"}。
    name字段啥东西都没有,解析时就会报错,求解这种如何处理。
    怪盗kidou:@喜欢而非坚持 这格式都不标准怎么容错 :flushed:
  • 自己找知己:最近刚好自己封装Gson工具类,遇到这个对象data和数组data的问题,原来可以全都用
    Type type = new TypeToken<Result<T>>(){}.getType();
    Result<T> result = gson.fromJson(json,type);
    解决。超赞。还是自己理解不深刻。
    d499c115489d:@喜欢而非坚持 这样解析list的时候不会泛型擦除么
  • 臧东来:精品啊,楼主 是在哪获取这些知识的。 能提供下链接吗。
    怪盗kidou:@臧东来 感谢打赏 :smile:
    怪盗kidou:@臧东来 IDE的自动完成、源码、注释、官方WIKI :flushed:
  • 2f8b3ab07efe:收益非浅,原来是我不会用,当成一直埋怨后端太坑...是我错了!感谢先生:grin:
    怪盗kidou:@青鱼德库拉 哈哈,只要按规则出接口就好
  • 小草房Fang:你好, 我这边遇到一个问题, 我数据库存的json, 我需要取出来跟已有的一些参数再拼成一个json串, 不知道怎么操作. 因为取出来的json是个字符串.
    怪盗kidou:@小草房Jason “{\“id\":"+id +",dataList:"+jsonArray+"}" 不就行了?,拼个json字符串应该不难吧
    小草房Fang:@怪盗kidou 比如我在数据库里取出一个json array 要作为list跟一个id号拼成一个jsonobject这个怎么操作?
    怪盗kidou:@小草房Jason 描述得不是很清楚,你没有一个简易的例子啥的,根据情况可以采用的方法自然也不一样,当然最通用和保险的作法就是解析一遍,在加入了你想要的参数后再生成。
  • Neogx:强大,看了之后发现自己真的不会用GSON,感谢楼主分享!
  • ibrucekong:写的很好,学习了 :smile:
  • 李斯维:谢的挺好的。
    弱弱地问一下,是不是只要从`json`解析到带有泛型的类就必须使用`TypeToken`啊?没有其他的方法了么?
    怪盗kidou:@545a3c856c5f 也不是,TypeToken只是一个工具类用来帮助你获泛型信息,当然你也可以自已经获取泛型信息,比如
    TypeToken<ArrayList<User>>(){}.getType() 你也可以用 new ArrayList<User>{}..getClass().getGenericSuperclass(); 代替,你愿意用哪个?
  • Hello_Light:小白请教一个问题,如果只需要获取titles、settime字段的数据,应该怎么做?
    {"total":1,"rows":[{"id": "3" ,"titles": "明天放假" ,"settime": "2016/5/5 17:30:24" ,"states": "1" ,"fqBumen": "信息中心" }]}
    怪盗kidou:@Hello_Light 话说上面明明就有一个User的例子。。。。。。。
    public class Result{
    public int total;
    public List<Data> rows;
    }
    public class Data{
    public String id;
    public String titles;
    public String settime;
    public String states;
    public String fqBumen;
    }

    Result result = gson.fromJson("{.......}",Result.class);
    for (Data data:result.rows){
    // TODO
    }
  • jasonkxs:涨姿势了~
  • 0eb3f1fa379c:楼主,看完第4篇,还是不知道怎么优雅的处理。由于设计成了Result<T> 泛型模式,这个T有可能是User,String、或者是List或者其它。但是这个"data",有值的时候正常,没值的时候就报错了。按照第四篇自己接管反序列,都是要传入具体的Class,但是这个T是不确定的,应该怎么传呢?还请博主赐教下。
    怪盗kidou:@咖啡上次 那你应该和你们的后台好好谈谈了,不要试着去自己搞定,一个两个特殊的还要,每个都这样你的进度和代码质量都会严重下降,费力不讨好,至于T,在每个接口中都是不一样的,但是他是确定的,在这个接口中是User,那第四篇的用法中就是User,是String那就是String
  • 0eb3f1fa379c:刚接触Gson,请教个问题哈。对于{"code":"0","message":"success","data":{}}这种的,正常情况下data里面是有值的,{"code":"0","message":"success","data":{"id" : 5,"user_name" : "aa"}},这时我传入的是User。但是非正常的时候,server返回的是这样的{"code":"0","message":"success","data":""},也就是data里啥也没有,由于我事先传入的是User,所以在解析的时候就报错了。对于data这种可能有值可能没值的,请教下楼主怎么设计,谢了。
    0eb3f1fa379c:@咖啡上次 上面好像提到这个问题了,我先看看
    怪盗kidou:@咖啡上次 这是后台设计的问题,没有也不该是"",而是{} 或Null才对,具体见第4篇
  • 聪明叉:注意到你的泛型是这么写的,网上大多是都是这么写的

    new TypeToken<T>(){}

    因为TypeToken的构造函数是protected, 所以要加{}, 等于是写了个匿名内部类, 这不是扯淡吗?别人protected了, 就肯定提供了方法给你用啊

    Type type = TypeToken.get(MovieEntity.class).getType();

    可以这样拿到Type, 正常调用gson的fromJson, 也可以拿到TypeAdapter后直接调用fromJson

    TypeAdapter<MovieEntity> adapter = gson.getAdapter(TypeToken.get(MovieEntity.class));
    MovieEntity movieEntity = adapter.fromJson(json);
    聪明叉:@怪盗kidou 如果哪句话冒犯了你, 在这里说句不好意思, 我这边用TypeToken.get方法并没有什么不妥啊
    聪明叉:@怪盗kidou 哥, 我真没看出了我这是喷, 哪句语气不对吗?
    我看了Retrofit的GsonConverterFactory的源码并且跳到TypeToken的源码里看了

    TypeToken.get方法

    public static <T> TypeToken<T> get(Class<T> type) {
    return new TypeToken<T>(type);
    }

    不也是new了TypeToken吗? 并不需要继承啊
    怪盗kidou:@聪明叉

    我只能呵呵了,喷之前先确认一下你自己真的搞懂了么

    首先Class本身就实现了Type接口,所以你的

    Type type = TypeToken.get(MovieEntity.class).getType();
    根本就是多余的:
    Type type = MovieEntity.class;
    也就是说
    TypeAdapter<MovieEntity> adapter = gson.getAdapter(TypeToken.get(MovieEntity.class));

    也可以直接写成

    TypeAdapter<MovieEntity> adapter = gson.getAdapter(MovieEntity.class);

    TypeToken存在的意义就是为了获取带泛型而存在的,为啥人家设计成protected而不是private或者default?protected的目的就是让你继承!你可以试试不使用继承TypeToken的方式用TypeToken.get获取一下Result<User>的Type试试?


    官方文档:https://github.com/google/gson/blob/master/UserGuide.md#TOC-Serializing-and-Deserializing-Generic-Types
  • MycroftWong::+1:🏻深受PHP的“毒害”,看到文章很有用
    怪盗kidou:@MycroftWong :joy:
  • 龙猫跑跑跑:非常感谢
  • GoodGoodStuday: 终于等到 大哥回复了。。。 在路上呢
  • GoodGoodStuday:大哥,今天碰得到你这个问题了。。举个例子: data里面有数据的时候,,{“com”:"9999","sat":"000","data":{"name":"zss","age":"23"}} data里面无数据的时候,,{“com”:"9999","sat":"000","data":[]} .... 有数据的时候是对象形式的,,无数据的时候,是数组的形式,,,这怎么解析啊,, 貌似的泛型 不起作用啊
    Jafir:@小宝拜财神 如果是这种情况的话,你可是这样试试。就是把data先数据类型弄成 String,然后先解析一层(就是除了data,其他都按照类型解析出来了),然后再解析一次data ,根据data自字符串contains [ 或者 { 来判断数据类型,进而转换。
    大致意思如下代码:
    //test1

    String t1 = "{\"code\":9999,\"message\":\"000\",\"data\":{\"name\":\"zss\",\"age\":\"23\"}}";
    String t2 = "{\"code\":9999,\"message\":\"000\",\"data\":[]}";


    Result r1 = gson.fromJson(t1, Result.class);
    KJLoger.debug(r1.toString());
    Result r2 = gson.fromJson(t2, Result.class);
    KJLoger.debug(r2.toString());

    if(r2.data.contains("[")){
    //空
    }else if(r2.data.contains("{")){
    Person person = gson.fromJson(r2.data,Person.class);
    }
    然而,如果是这样的话,那么你的通过服务器数据解析的bean并不能是你的Javabean,
    所以的确是很麻烦的。 如果想要统一 ,让服务器解析的数据 直接变成你需要用的Javabean,那么 你要和后台沟通好。
    GoodGoodStuday:@怪盗kidou 刚到家,是呀,一下午又在摸索,又在商量的,后台那边不愿意改,所以,都不知道怎么弄了。哎
    怪盗kidou:@小宝拜财神 是的,其实这种应该由服务器端来解决,当出错时,或没有数据时,让他们不要返回data字段,或者返与正常是相同的数据类型,或NULL,如果都自己处理的话就,最近代码乱得一逼
  • 键盘男:Type userListType = new TypeToken<Result<List<User>>>(){}.getType();
    Result<List<User>> userListResult = gson.fromJson(json,userType);

    写错了。userListType 写错成 userType
    怪盗kidou:@苦逼键盘男kkmike999 已修改 :smile:
  • haotie1990:解决了很多疑惑,非常感谢你
    怪盗kidou:@haotie1990 很荣幸
  • 81ad24cedc67:你好, 我有一个问题 json不会返回所有 自定义对象里的参数, 而这些参数又是依靠判断json返回的其它参数值来赋值. 需要怎么做呢?
    81ad24cedc67:@Jafir :smile_cat: 感谢感谢
    Jafir:@somehand 你先把json解析出来变成bean,如果里面服务器返回的数据没有 那么bean里面就是null,然后你拿到bean就可以自己处理咯,通过其他参数然后计算还是干嘛,最后赋值就行了撒
  • 安之密语:最近项目用到retrofit的gson来解析
    但是服务器返回这坑爹数据,一条是以前的数据,一条是现在的
    能不能用gson同时解析啊?
    头疼死我了,现在我只能用object来忽略它
    有没有解决办法

    [3,7,"26368","player"]
    {"id": 111,"name": "测试"}
    怪盗kidou:@RitzoneX 这个确实没有好办法,连最外层就不一样,后台的问题
  • C_zx:很不错 干货
  • Diffey:将数据解析为List<String>不需要TypeToken,Gson对于基本类型和String可以转成功,其他的会转为LinkedTreeMap丢失类型信息。
    Jafir:@Diffey 我也刚测试过 是的
    怪盗kidou:@Diffey 嗯,对于普通的类型是可以通直接使用List,但其它的如果返回Map,说实话就没有了意义,选择List<String> 一方面也是可以让代码更简洁明了。
  • GoodGoodStuday:public class Result<T> {

    private String code;
    private String mess;
    private T data;

    public String getCode() {
    return code;
    }

    public void setCode(String code) {
    this.code = code;
    }

    public T getData() {
    return data;
    }

    public void setData(T data) {
    this.data = data;
    }

    public String getMess() {
    return mess;
    }

    public void setMess(String mess) {
    this.mess = mess;
    }
    }

    大哥谢谢你,又一次这么细致的解答。还有就是你封装的那result<T> 写完整就是我写的上面那个样嘛,构造方法什么的都不需要了??? 今天还把这个运用到项目里面去了呢,没成,刚才看了你写的那,知道怎么写了。。大哥,谢谢你。。
    GoodGoodStuday:@怪盗kidou 嗯嗯知道了。。
    怪盗kidou:@小宝拜财神 我是写Android的,所有一般都是定义成public 的,Gson不管你的那种形式,最终都是根据字段设值,不会调你的方法的。Gson只需要一个空构造方法就行用于反射生成对象
  • GoodGoodStuday:public static class DataResponse<T> {
    public int code;
    public String message;
    public T data;
    }
    大哥,你好,封装的这个,为什么是静态类啊??? 怎么使用呢,糊涂了。还望大哥解答一下啊
    怪盗kidou: @小宝拜财神 😓 DataResponse<User> userResp = new DataResponse();,这种写法见过吧,这时候的userResp.data就是User类型的,就这么个意思。
    GoodGoodStuday:大哥,恕小弟愚笨,想了想,还是没明白过来 :joy:

    怪盗kidou:@小宝拜财神 一时失误,我写例子的时候为了简便,没有写到单独的文件里,所以有static,你可以继续往下看一点点,和你用list是一个用法
  • d2301532d2d9:你好,能请教一个问题吗?
    在你最后一个例子的时候,服务器返回的数据中data字段类型不固定,比如请求成功data是一个List,不成功的时候是String类型,这样前端在使用泛型解析的时候,怎么去处理呢?
    d21bf0d996c5:@怪盗kidou 2、出错时不返回该字段。 请教下,如果不返回该字段,正常的是:
    public class UserResponse {
    public int code;
    public String message;
    public User data;
    }
    出错时是:
    public class UserResponse {
    public int code;
    public String message;
    }

    gson.fromJson(responseString, 对象.class),实际的“对象”该怎么写呢,再反序列化之前也不知道是正常的还是错误的。
    d2301532d2d9:@怪盗kidou 谢谢了,等你第四篇,刚刚仔仔细细的看了这三篇,受益匪浅,还真不知道Gson可以这样用,如果早点看运用到项目中可以免去好多麻烦啊 :blush: :blush:
    怪盗kidou:@遵命笔下 这样的话实那上在服务器上返回的数据不合理,没有保证数据的一致性,仅仅通过泛型是不能解决的。 使用泛型是我们知道这个接口的类型,但正确和错误时有两种,那说明类型是不固定,一个泛型变量不能承载两种类型的。解决办法:1、出错时返回空数组 [];2、出错时不返回该字段。3、使用TypeAdapter,可以参考上面5楼的评论,或者等待接下来的 第4篇文章,今明天就会出。
  • RidingWind2023:有很大收获 谢了
    怪盗kidou: @海上牧云 😄
  • 46f93ff1c93c:看了这篇文章之前的一些疑惑解决了不少,继续关注
  • 键盘男:不错不错(✪㉨✪)   平时用gson都没注意这些特性。请教一个问题,服务器返回"age":"",但bean定义int age,gson解析就报错。这个问题又解决方法吗?
    键盘男:@怪盗kidou 好详细啊!!!我今晚试试,现在有点忙。谢谢大神!
    怪盗kidou:@苦逼键盘男kkmike999
    Gson在GsonBuilder中提供了一个叫 “registerTypeAdapter”的API(之后文章会单独讲),当你需要对某个类型按自己的意思进行时,可以使用该API,比如你这里,需要对int进行处理,但有可能返回空字符串,那么流程属于反序列化,所以:
    Gson gson = new GsonBuilder().registerTypeAdapter(int.class, new JsonDeserializer<Integer>() {
    @ Override
    public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    try {
    return json.getAsInt();
    } catch (Exception e) {
    return 0;
    }
    }
    }).create();
    String json = "{\"name\":\"怪盗kidou\",\"age\":\"\"}";
    User user = gson.fromJson(json, User.class);
    System.out.println(user.age); // 0
  • e19752dfa551:你好,我遇到了一个问题。@SerializedName(value = "emailAddress", alternate = {"email", "email_address"}),这句话在 AS 里编译不过,提示找不到 alternate 。我的 GSON 版本是 2.2.4。是不是版本原因啊?
    bb500d92fb52:@程序猴 用2.6.2吧
    怪盗kidou:@程序猴 你可以查看一下源码,看SerializedName 是不是提供了两个属性value和alternate,如果没有就是版本问题,我使用的版本是2.4
  • Neulana:楼主讲得很棒!可不可以写篇关于retrofit的知识呢
    怪盗kidou:@ghysrc 但Retrofit用法确实是很简单,注要就是注解的使用,并且这些注解都是和HTTP的内容相关联的
    ghysrc:@怪盗kidou 写的详细又很好懂啊,之前只是用 Gson 解析,从没想到竟然还有那么多功能。Retrofit最近确实越来越火了,不过详细的文章并没有多少,看了 Gson 系列,很期待楼主啊。
    怪盗kidou:@加速度猫 可以的,感谢建议,正好不知道各位都对啥感兴趣呢
  • 小草房Fang:fastjson支持这种自动解析设值吗?
    怪盗kidou:@小草房Jason 你说问用fastjson怎么把json反序列化为Result<User>吗?

    JSON.parseObject(jsonObject, new TypeReference<Result<User>>() {}.getType());

    总共就那么几个方法,看方法签名也大概能猜哪个方法吧
    小草房Fang:@怪盗kidou 正常是code, message, data{} JSON.parseObject() 这里面如果传Result<User>.class,怎么解析这个data{}
    怪盗kidou:@小草房Jason fastjson 也支持直接序列化和反序列化POJO,` JSON.parseObject()` 和 `JSON.parseArray()` ,也支持范型。

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

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