zoukankan      html  css  js  c++  java
  • 你真的会用Gson吗?Gson使用指南(一)

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

    JSON (官网) 是一种文本形式的数据交换格式,它比XML更轻量、比二进制容易阅读和编写,调式也更加方便。其重要性不言而喻。解析和生成的方式很多,Java中最常用的类库有:JSON-JavaGsonJacksonFastJson等,本次我向大家介绍的是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

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

    基本数据类型的生成

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

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

    那么对于jsonemail_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_addressemailemailAddress)都中出现任意一个时均可以得到正确的结果。

    注:当多种情况同时出时,以最后一个出现的值为准。

    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中的NumberbooleanObjectString,现在说一下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>>,其它同理。

    下面给个两个完整的例子,UserList<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;

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

    作者:怪盗kidou

    链接:https://www.jianshu.com/p/e740196225a4

    来源:简书

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    1012 The Best Rank (25 分)(排序)
    1011. World Cup Betting (20)(查找元素)
    1009 Product of Polynomials (25 分)(模拟)
    1008 Elevator (20 分)(数学问题)
    1006 Sign In and Sign Out (25 分)(查找元素)
    1005 Spell It Right (20 分)(字符串处理)
    Kafka Connect 出现ERROR Failed to flush WorkerSourceTask{id=local-file-source-0}, timed out while wait
    flume、kafka、avro组成的消息系统
    Java23种设计模式总结【转载】
    Java编程 思维导图
  • 原文地址:https://www.cnblogs.com/heqingxiaohuo/p/12383683.html
Copyright © 2011-2022 走看看