Gson使用说明书_gson文档-程序员宅基地

技术标签: java  Java工具箱  组合模式  开发语言  

public class GsonUtil {
    

    private static final Gson GSON = new Gson();

    public static String toJSONString(Object object) {
    
        return object == null ? null : GSON.toJson(object);
    }

    public static <T> T parseObject(String text, Class<T> clazz) {
    
        return text == null ? null : GSON.fromJson(text, clazz);
    }

    public static <T> List<T> parseArray(String text, Class<T> clazz) {
    
        if (StringUtils.isEmpty(text)) {
    
            text = "[]";
        }

        return (List) GSON.fromJson(text, TypeToken.getParameterized(ArrayList.class, new Type[] {
    clazz}).getType());
    }

    public static <K, V> Map<K, V> parseMap(String text) {
    
        if (StringUtils.isEmpty(text)) {
    
            text = "{}";
        }
        return (Map<K, V>) GSON.fromJson(text, new TypeToken<Map<K,V>>(){
    }.getType());
    }
}

1 Gson的基本用法

Gson提供了fromJson() 和toJson() 两个直接用于解析和生成的方法,前者实现反序列化,后者实现了序列化。

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

1.1 基本数据类型的解析

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

1.2 基本数据类型的生成

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

1.3 POJO类的生成与解析

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Role implements Serializable {
    
    private static final long serialVersionUID = -1767327914553823741L;
    private Integer id;
    private String role;
    private String desc;
}

生成json

Gson gson = new Gson();
Role role = new Role(1001, "admin", "超级管理员");
String str = gson.toJson(role); // {"id":1001,"role":"admin","desc":"超级管理员"}

解析json

Gson gson = new Gson();
String jsonStr = "{\"id\":1001,\"role\":\"admin\",\"desc\":\"超级管理员\"}";
Role role = gson.fromJson(jsonStr, Role.class); // Role(id=1001, role=admin, desc=超级管理员)

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

期望的JSON:

{
    "id":1001,"role":"admin","descRole":"超级管理员"}

实际的JSON:

{
    "id":1001,"role":"admin","desc_role":"超级管理员"}

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

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

@SerializedName("desc_role")
public String descRole;

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

{
    "id":1001,"role":"admin","descRole":"超级管理员"}
{
    "id":1001,"role":"admin","desc_role":"超级管理员"}
{
    "id":1001,"role":"admin","desc":"超级管理员"}
2.1 为POJO字段提供备选属性名

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

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Role implements Serializable {
    
    private static final long serialVersionUID = -1767327914553823741L;
    private Integer id;
    private String role;
    @SerializedName(value = "descRole", alternate = {
    "desc", "desc_role"})
    private String descRole;
}

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

Gson gson = new Gson();
String json = "{\"id\":1001,\"role\":\"admin\",\"descRole\":\"超级管理员-1\",\"desc_role\":\"超级管理员-2\",\"desc\":\"超级管理员-3\"}";
Role role = gson.fromJson(json, Role.class); // Role(id=1001, role=admin, descRole=超级管理员-3)

3 Gson中使用泛型

3.1 JSON字符串数组

["zhao","fang","sun"]

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

Gson gson = new Gson();
String jsonArray = "[\"zhao\",\"fang\",\"sun\"]";
String[] strs = gson.fromJson(jsonArray, String[].class);

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

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

Gson gson = new Gson();
String jsonArray = "[\"zhao\",\"fang\",\"sun\"]";
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {
    }.getType());

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

3.2 泛型解析对接口POJO的设计影响

泛型的引入可以减少无关的代码,如接口返回的数据分为两类:

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

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

public class RoleResponse {
    
    public int code;
    public String message;
    public Role data;
}

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

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

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

4 Gson的流式反序列化

4.1 自动方式

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

Gson.toJson(Object);
Gson.fromJson(Reader,Class);
Gson.fromJson(String,Class);
Gson.fromJson(Reader,Type);
Gson.fromJson(String,Type);

Gson.fromJson(Reader,Class);

Gson gson = new Gson();
String json = "{\"id\":1001,\"role\":\"admin\",\"descRole\":\"超级管理员\"}";
Role role = gson.fromJson(json, Role.class); // Role(id=1001, role=admin, descRole=超级管理员)
System.out.println(role);

4.2 手动方式

手动的方式就是使用stream包下的JsonReader类来手动实现反序列化。

 String json = "{\"id\":1001,\"role\":\"admin\",\"desc\":\"超级管理员\"}";
 Role role = new Role();
 JsonReader reader = new JsonReader(new StringReader(json));
 reader.beginObject(); // throws IOException
 while (reader.hasNext()) {
    
     String s = reader.nextName();
     switch (s) {
    
         case "id":
             role.setId(reader.nextInt());
             break;
         case "role":
             role.setRole(reader.nextString()); //自动转换
             break;
         case "desc":
             role.setDescRole(reader.nextString());
             break;
     }
 }
 reader.endObject(); // throws IOException
 System.out.println(role.getId());  // 1001
 System.out.println(role.getRole());   // admin
 System.out.println(role.getDescRole()); // 超级管理员

5 Gson的流式序列化

5.1 自动方式

在这里插入图片描述

Gson gson = new Gson();
Role role = new Role(1001, "admin", "超级管理员");
gson.toJson(role, System.out); // 写到控制台 -> {"id":1001,"role":"admin","descRole":"超级管理员"}

5.2 手动方式

JsonWriter writer = new JsonWriter(new OutputStreamWriter(System.out));
        writer.beginObject() // throws IOException
                .name("id").value(1001)
                .name("role").value("admin")
                .name("descRole").nullValue() //演示null
                .endObject(); // throws IOException
        writer.flush(); // throws IOException
        // {"id":1001,"role":"admin","descRole":null}

6 使用GsonBuilder导出null值、格式化输出、日期时间

一般情况下Gson类提供的 API已经能满足大部分的使用场景,但我们需要更多更特殊、更强大的功能时,这时候就引入一个新的类 GsonBuilder。GsonBuilder从名上也能知道是用于构建Gson实例的一个类,要想改变Gson默认的设置必须使用该类配置Gson。

Gson gson = new GsonBuilder()
               //各种配置
               .create(); //生成配置好的Gson

6.1 GsonBuilder导出null值

Gson在默认情况下是不会导出值null的键的,如:

public class Role implements Serializable {
    
    private static final long serialVersionUID = -1767327914553823741L;
    private Integer id;
    private String role;
    @SerializedName(value = "descRole", alternate = {
    "desc", "desc_role"})
    private String descRole;
    public Role(Integer id, String role) {
    
        this.id = id;
        this.role = role;
    }
}

Gson gson = new Gson();
Role role = new Role(1001, "admin");
String str = gson.toJson(role); // {"id":1001,"role":"admin"}

可以看出,descRole字段是没有在json中出现的,当我们在调试是、需要导出完整的json串时或API中要求没有值必须用Null时,就会比较有用。

Gson gson = new GsonBuilder()
                .serializeNulls()
                .create();
Role role = new Role(1001, "admin");
String str = gson.toJson(role); // {"id":1001,"role":"admin","descRole":null}

6.2 格式化输出、日期时间及其它

01 setDateFormat 自定义日期格式
 Gson gson = new GsonBuilder()
                //序列化null
                .serializeNulls()
                // 设置日期时间格式,另有2个重载方法
                // 在序列化和反序化时均生效
                .setDateFormat("yyyy-MM-dd")
                .create();
        Role role = new Role(1001, "admin", "超级管理员", new Date());
        String str = gson.toJson(role); // {"id":1001,"role":"admin","descRole":"超级管理员","date":"2022-03-05"}
02 setPrettyPrinting 格式化输出
   Gson gson = new GsonBuilder()
                //序列化null
                .serializeNulls()
                // 设置日期时间格式,另有2个重载方法
                // 在序列化和反序化时均生效
                .setDateFormat("yyyy-MM-dd") 
                // 格式化输出
                .setPrettyPrinting()
                .create();
  Role role = new Role(1001, "admin", "超级管理员", new Date());
  String str = gson.toJson(role); // 
   /*{
       "id": 1001,
           "role": "admin",
           "descRole": "超级管理员",
           "date": "2022-03-05"
   }*/

7 字段过滤的几种方法

字段过滤Gson中比较常用的技巧,在处理业务逻辑时可能需要在设置的POJO中加入一些字段,但显然在序列化的过程中是不需要的,并且如果序列化还可能带来一个问题就是 循环引用 ,那么在用Gson序列化之前为不防止这样的事件情发生,你不得不作另外的处理。

以一个商品分类Category 为例:

{
    
  "id": 1,
  "name": "电脑",
  "children": [
    {
    
      "id": 100,
      "name": "笔记本"
    },
    {
    
      "id": 101,
      "name": "台式机"
    }
  ]
}

一个大分类,可以有很多小分类,那么显然我们在设计Category类时。Category本身既可以是大分类,也可以是小分类。

public class Category {
    
    public int id;
    public String name;
    public List<Category> children;
}

但是为了处理业务,我们还需要在子分类中保存父分类,最终会变成下面的情况:

public class Category {
    
    public int id;
    public String name;
    public List<Category> children;
    //因业务需要增加,但并不需要序列化
    public Category parent; 
}

但是上面的parent字段是因业务需要增加的,那么在序列化是并不需要,所以在序列化时就必须将其排除,那么在Gson中如何排除不符合条件的字段呢?下面提供4种方法。

7.1 基于@Expose注解

@Expose 注解从名字上就可以看出是暴露的意思,所以该注解是用于对外暴露字段的。可是我们以前用Gson的时候也没有@Expose 注解还不是正确的序列化为JSON了么?是的,所以该注解在使用new Gson() 时是不会发生作用。毕竟最常用的API要最简单,所以该注解必须和GsonBuilder配合使用。

使用方法: 简单说来就是需要导出的字段上加上@Expose 注解,不导出的字段不加。

@Expose //
@Expose(deserialize = true,serialize = true) //序列化和反序列化都都生效,等价于上一条
@Expose(deserialize = true,serialize = false) //反序列化时生效
@Expose(deserialize = false,serialize = true) //序列化时生效
@Expose(deserialize = false,serialize = false) // 和不写注解一样

注:根据上面的图片可以得出,所有值为true的属性都是可以不写的(默认值是true)。

拿上面的例子来说就是

public class Category {
    
    @Expose public int id;
    @Expose public String name;
    @Expose public List<Category> children;
    //不需要序列化,所以不加 @Expose 注解,
    //等价于 @Expose(deserialize = false,serialize = false)
    public Category parent; 
}

在使用Gson时也不能只是简单的new Gson()了。

Gson gson = new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()
        .create();
gson.toJson(category);

7.2 基于策略(自定义规则)

基于策略是利用Gson提供的ExclusionStrategy接口,同样需要使用GsonBuilder,相关API 2个,分别是addSerializationExclusionStrategy 和addDeserializationExclusionStrategy 分别针对序列化和反序化时。这里以序列化为例。

Gson gson = new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
    
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
    
                // 这里作判断,决定要不要排除该字段,return true为排除
                if ("finalField".equals(f.getName())) return true; //按字段名排除
                Expose expose = f.getAnnotation(Expose.class); 
                if (expose != null && expose.deserialize() == false) return true; //按注解排除
                return false;
            }
            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
    
                // 直接排除某个类 ,return true为排除
                return (clazz == int.class || clazz == Integer.class);
            }
        })
        .create();
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zs18753479279/article/details/123289307

智能推荐

ASP.NET中的AutoPostBack和IsPostBack以及EnableViewState三者的运用以及区别_autopostback属性与ispostback属性的区别-程序员宅基地

文章浏览阅读927次。相信大家都在开始学ASP.NET的时候都会被它里面的几个属性搞得晕头转向,本文向各位学者介绍AutoPostBack和IsPostBack以及EnableViewState三者的运用以及区别,话不多说,让咱们来看例子来说明问题1.IsPostBack新建一个网站,在页面放置一个DropDownList和一个按钮,我希望点击按钮之后添加往DropDownList之中添加一条内容//Def_autopostback属性与ispostback属性的区别

eNSP模拟器路由器无法正常启动一直显示“#”——问题解决方法_ensp启动路由器一直显示井号 win7-程序员宅基地

文章浏览阅读2.1k次。eNSP模拟器路由器无法正常启动一直显示“#”——问题解决方法_ensp启动路由器一直显示井号 win7

TEX Quotes_c++tex quotes-程序员宅基地

文章浏览阅读963次。Think: 引号转换题, 因为不知道长度,所以用getchar() 但是不明白 为什么代码 在编译器上会 报错。。。 但是 提交是AC的。。 所以思路就是,读取字符然后判断时候是 ” , 如果是的话在判断 是第几次出现(也就是判断 是左 还是 右 引号),然后 改变 输出字符。 题目虽然很长 但是是水题 。。。Problem Description TEX is a type_c++tex quotes

打印 RichTextBox_richtextbox中的图片可以打印吗-程序员宅基地

文章浏览阅读1.5k次。创建 RichTextBoxPrintCtrl 控件下面的示例介绍了如何扩展 RichTextBox 类,以及如何使用 EM_FORMATRANGE 打印 RichTextBox 控件的内容。1.在 Visual C# .NET 中,新建一个名为 RichTextBoxPrintCtrl 的类库项目。默认情况下创建 Class1.cs。2.将 Class1._richtextbox中的图片可以打印吗

【笔记】AndroidX RecyclerView添加滚动条_androidx 滚动条-程序员宅基地

文章浏览阅读662次。添加以下代码即可android:scrollbars="vertical"其他办法无效._androidx 滚动条

学习笔记_lix重启数据库命令-程序员宅基地

文章浏览阅读525次。###重新创建索引SQL> CREATE INDEX rname_idx2 ON hr.region s (region_name)3 PARALLEL 4;索引丢失时,更为快速、简单的方法是重新创建而不是尝试恢复索引###重新创建口令验证文件1. 使用OS 验证登录到数据库。2. 将REMOTE_LOGIN_PASSWORDFILE参数设置为NONE 并重新启动数据库。_lix重启数据库命令

随便推点

初学SpringCloud:建设EurekaServer集群(新建一个EurekaServer模块)_eureka如何新增server节点到集群里-程序员宅基地

文章浏览阅读154次。目录1、背景介绍2、本篇博客的目的3、新增一个7002端口的EurekaServer模块,展示一下这个模块的文件结构4、新增一个7002端口的EurekaServer模块,展示一下这个模块的POM文件5、新增一个7002端口的EurekaServer模块,展示一下这个模块的application文件6、新增一个7002端口的EurekaServer模块,展示一下这个模块的主启动类。我的上一篇博客:初学SpringCloud:建立使用90端口的微服务模块,注册到服务注册中心_eureka如何新增server节点到集群里

matlab 简单的水轮机系统的模糊pid控制仿真_matlab仿真中有模糊pid模块吗-程序员宅基地

文章浏览阅读1k次。模糊推理系统编辑器用于设计和显示模糊推理系统的一些基本信息,如推理系统的名称,输入、输出变量的个数与名称,模糊推理系统的类型、解模糊方法等。该编辑器提供一个友好的人机图形交互环境,用来设计和修改模糊推理系中各语言变量对应的隶属度函数的相关参数,如隶属度函数的形状、范围、论域大小等,系统提供的隶属度函数有三角、梯形、高斯形、钟形等,也可用户自行定义。打开模糊推理系统编辑器,在MATLAB的命令窗(command window)内键入:fuzzy 命令,弹出模糊推理系统编辑器界面,_matlab仿真中有模糊pid模块吗

JAVA五--面向对象基础_java实验5面向对象基础-程序员宅基地

文章浏览阅读117次。基本概念编程语言总共有两大类,一类以汇编语言和c语言为首的最底层的用于嵌入式编程的面向过程编程,另一类就是以JAVA、C++、Python为首的面向对象编程。面向编程和面向对象的区别有很多,优缺点也很明显。1.运行效率,面向过程的执行效率比面向对象的执行效率更高,因为他更适用于底层的硬件上,去命令计算机做事情,面向对象是基于操作系统来控制电脑进行执行的,效率慢一点,但是思想更高级。2.运用场景,软硬件编程的区别三大特征封装、继承、多态或者可以吧抽象也加上1.封装:把相关的数据封装成一个“类”_java实验5面向对象基础

http消息当中,post和get两种请求方式的区别_get有局限性吗?-程序员宅基地

文章浏览阅读2.1k次。上一周一直在看有关微信小程序,订阅号,服务号,等微信的应用,没来的及继续学习,今个学习到javaweb 的servlet ,觉得有个地方,可以大家稍微注意一下,就是消息请求当中,GET 与 POST 这两个消息请求的方式,他们的区别在哪里,平常咱们都用的啥~首先,尼玛哥在网上查阅资料,大概是有以下几点的区别:1.Post传输的数据量大,可以达到2M,而Get方法由于受到URL长度的限制_get有局限性吗?

支付风控规则_支付宝风控规则-程序员宅基地

文章浏览阅读1.3k次。二:敢死队,不管新的还是老的,让人工智能知道我们大量的数据,越多越乱越好,这样我们做什么都是对的,因为我们什么都做,但不能一下太多,要有一个比例,比如10%到30%,偶尔还得停一下,给人工智能来一个丈二和尚。这种提交就通过了,但是很快又有风控,也是重复上面的步骤,就要你提交资料了,就是一些身份证、营业执照,单号就发快递面单就行,审核也很快,还是不行就联系客服协助处理。如果你的收款码用支付宝扫码,还能正常拉起,然后付1分钱,去账单里,就有下图这个单号了,复制这个去填上就行,可以一直保留,能一直用。_支付宝风控规则

基于SpringBoot+微信小程序的失物招领小程序_微信小程序失物招领-程序员宅基地

文章浏览阅读261次。我们基于Spring Boot和微信小程序开发的失物招领小程序旨在提供一个便捷的平台,帮助用户在失去物品或找到物品时能够迅速发布或查询相关信息。随着城市化进程的加速和人口的增长,失物招领成为了一个常见的需求,但传统的失物招领方式存在着信息传递不及时、不便捷等问题。因此,我们致力于通过技术手段解决这些问题,为用户提供一个高效、安全、便捷的失物招领平台。_微信小程序失物招领

推荐文章

热门文章

相关标签