吃透FastJSON,认准此文!

JSON 介绍

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。它采用一种 "键 : 值" 对的文本格式来存储和表示数据,在系统交换数据过程当中经常被使用,是一种理想的数据交换语言。前端

"XML 的时代已通过去,如今是 JSON 的时代" 。相信如今这个观点不少人已经默默认同,那么咱们是否有认真思考过为何如今 JSON 可以顶替 XML 的地位。咱们来简单看下两种的表示方式:java

<?xml version="1.0" encoding="gb2312"?>
<class>
    <stu id="001">
        <name>杨过</name> 
        <sex></sex>
        <age>20</age>
    </stu>  
    <stu id="002">
        <name>小龙女</name>    
        <sex></sex>
        <age>21</age>
    </stu>
</class>
[
    {
        "id""001",
        "name""杨过",
        "sex""男",
        "age""20"
    },
    {
        "id""002",
        "name""小龙女",
        "sex""女",
        "age""21"
    }
]

两种方式都是用来描述简单的班级信息,信息很少,可是明显能够看出 JSON 比 XML更加简洁。具体区别可为如下几点:编程

  • 可读性: JSON 和 XML 的可读性可谓不相上下,一边是简易的语法,一边是规范的标签形式,很难分出胜负
  • 可扩展性: XML 天生有很好的扩展性,JSON 固然也有,所以 XML 能扩展的,JSON 也能够扩展
  • 编码难度: XML 有丰富的编码工具,好比 DOM4J,JDom 等,JSON 也提供许多工具。可是在没有工具的状况下,由于 XML 有不少结构上的字符,编程难度相对较高。
  • 解码难度: XML 的解析须要考虑到子节点父节点,难度较大,而 JSON 的解析难度几乎为 0,看上去就能理解数据结构

JSON 认知

JSON 具备如下形式
  • JSON 对象

源网侵删json

{
    "id""002",
    "name""小龙女",
    "sex""女",
    "age""21"
}

这就是一个简单的JSON 对象,咱们观察能够得出 JSON 的一些语法:设计模式

  1. 数据在花括号中 []
  2. 数据以 键 : 值 对的形式出现(其中键多以字符串的形式出现,值可为字符串,数值,以及 JSON 对象)
  3. 每两个 键 : 值 对以逗号分隔 , , 最后一个键值对需省略 ,

咱们按照上面的 3 点特征,即可很简单的构建出一个 JSON 对象数组

  • JSON 数组

图片源网侵删网络

["value1","value2","value3"]

数据结构

[
    {
        "id""001",
        "name""杨过",
        "sex""男",
        "age""20"
    },
    {
        "id""002",
        "name""小龙女",
        "sex""女",
        "age""21"
    }
]

数组的表示方式也很简单:ide

  1. 头尾由 [] 包裹
  2. 数据主键以 , 隔开
  • JSON 字符串

图片源网侵删工具

'{"id""001""name""杨过""sex""男""age""20"}'

JSON 字符串与 Java 的字符串很是类似。

  1. 它必须以 "" 或者 '' 包裹数据,支持字符串的各类操做
  2. 里面的数据格式能够为 json对象,也能够是 json数组亦或者是两个基本形式的组合变形

以上即是 JSON 的基本形式,JSON 可使用于各类语言,每一个语言皆有本身各自的JSON 实现方式。下面咱们主要来熟悉一下 Java 语言中的 FastJSON 的使用。

FastJSON

FastJSON 是由阿里巴巴工程师基于 JAVA 开发的一款 JSON 解析器和生成器,可用于将 Java 对象转换为其 JSON 表示形式,它还能够用于将 JSON 字符串转换为等效的 Java 对象。FastJSON 能够处理任意 Java 对象,包括没有源代码的预先存在的对象

FastJSON 使用十分方便,咱们只须要在 Maven 工程的 pom 文件中引入如下依赖便可:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
</dependency>

FastJSON API 的入口类是 com.alibaba.fastjson.JSON,经常使用的序列化操做均可以在JSON类上的静态方法直接完成。

图片

API

咱们已经在项目中引入 FastJSON 的依赖,和已经存在了一个用户类:

@Data
public class User {
    private int id;

    private String name;

    private String sex;
    
    private int age;
}
toJSONString(Object o)

这个方法平时最多见了,将JavaBean序列化成 JSON 文本

图片

咱们经过传入一个对象,即可以将对象转成 JSON 字符串,这里咱们传入的不只仅是JavaBean 还能够是一个 Map 对象

图片

传入一个 Map 对象 咱们一样能够获取到一个 JSON 字符串。List 对象也很适用:

图片

结果是一个标准的 JSONArray 的字符串

若是说 toJSONString(Object o) 的输出结果只有单调的一行让你看起来有点吃力,那么咱们可使用 toJSONString(Object o, boolean prettyFormat) 来让输出结果看起来舒服点:

图片

经过 JSON 自带的格式化,让输出结果看起来更加清晰,真是贴心~

有小伙伴估计想着这两种我平时都用腻歪了,哪里有的着在你这看,小菜一想,言之有理。那就介绍一下 toJSONString 的扩展用法。

JSON.toJSONString(Object object, SerializerFeature... features)

咱们能够看到这个方法里面有个参数 SerializerFeature...,可能感到有点陌生,确实,我也很陌生,咱们先来看下什么是 SerializerFeature,经过源码咱们能够发现 SerializerFeature 原来是个枚举类:

看到这张图咱们不要晕,里面虽然有不少实例,可是大部分都被 @deprecated 注释了,说明这些已经废弃了。那有哪些是咱们平时常常用到的呢:

对象 描述
SerializerFeature.UseSingleQuotes 使用单引号而不是双引号,默认为false
SerializerFeature.PrettyFormat 结果是否格式化,默认为false
SerializerFeature.WriteDateUseDateFormat 若是时间是data、时间戳类型,按照这种格式初始化时间 "yyyy-MM-dd HH:mm"
SerializerFeature.WriteMapNullValue 是否输出值为null的字段,默认为false
SerializerFeature.WriteClassName 序列化时写入类型信息,默认为false

使用案例:

  • SerializerFeature.UseSingleQuotes

使用单引号而不是双引号,默认为false

图片

  • SerializerFeature.PrettyFormat

结果是否格式化,默认为false

图片

  • SerializerFeature.WriteDateUseDateFormat

若是时间是data、时间戳类型,按照这种格式初始化时间 "yyyy-MM-dd HH:mm"

图片

经过这种方式咱们将日期输出成了固定的格式:yyyy-MM-dd HH:mm,有时候咱们不想获得这种格式那该怎么办,办法总会有的:

图片

这个方式支持自定义时间格式,可是用到方法是toJSONStringWithDateFormat(),这里须要注意下,不要到时候用错了方法,还说小菜 渣男 ~

  • SerializerFeature.WriteMapNullValue

是否输出值为null的字段,默认为false

这个用什么用处了,咱们应该很清楚开发规范中鼓励用JavaBean传递参数,尽可能减小经过 Map 传递参数,由于 Map 至关于一个黑盒,对于使用者来讲根本不知道里面存在哪些字段,而对于建立者来讲估计也会忘记里面存在哪些字段,为了解决这个痛,JSON 也推出了解决方法:

图片

经过普通方式的 toJSONString() 方法,空值仿佛被 吃掉 了,这极可能会成为一个开发灾难!

  • SerializerFeature.WriteClassName

序列化时写入类型信息,默认为false。这个方法能够在反序列化的时候用到,用法以下:

图片

经过这样咱们能够看到咱们序列化的对象是什么类型的。

上面这些即是 toJSONString 的扩展用法,小伙伴们有没有满满的收获~

vx 搜:小菜良记

更多干货值得关注,每篇都是初恋的味道,钉~

  • parseObject(String text)

上面说到的是 序列化,那么对应的即是 反序列化

反序列化就是把JSON格式的字符串转化为Java Bean对象。

用法十分简单,能够将一个标准的 JSON 字符串 转为一个 JSONObject 对象,因为JSONObject 类 实现了 Map 接口,所以咱们能够经过 get() 来获取到值。

咱们已经知道了 Map 的致命不足,因此咱们更但愿能获得一个 JavaBean 对象。

固然也是能够的!咱们经过传入咱们想要转换的对象类型,就能够获得咱们想要的JavaBean

除了 基本反序列化 以外,还有一种 泛型反序列化 可供使用

经过 泛型 ,咱们就能够不用传入一个 Class 对象,而直接获取到咱们的 JavaBean

FastJSON 序列化还有一个用处那即是进行 深克隆。有看过我前面文章的小伙伴们相信如今对软件设计模式都有必定的了解了,其中原型模式涉及到的 深克隆 和 浅克隆

浅克隆的实现方式十分简单,咱们只须要实现 Cloneable 接口,而后重写 clone()方法 :

图片

结果中咱们看到的好人卡 都是属于小王的,这就是 浅克隆 的弊端的了。咱们想要实现 深克隆 有许多种方式:

  • 手动为引用属性赋值
  • 借助 FastJSON
  • 使用 java 流的序列化对象

方法有许多,咱们重点看下 FastJSON 的实现方式,经过 FastJSON 的反序列化,咱们获得的两个对象其实是不一样的,这也很方便的实现了 深克隆

parseArray(String text)

这是一个将 JSON字符串 转为 JSONArray 的方法

图片

一样咱们也能够经过使用 泛型序列化 来实现一样的功能:

图片

这种方式有个坑就是:咱们使用 parseArray() 这个方法的时候第二个参数须要传入咱们要反序列化的对象类型,可是咱们这里须要传入的是数组,不知道你有没有为数组里放了两个同样的type感到奇怪?没错,这就是这个方法的坑,咱们 List 里面有多少个对象, Type[] 这个数组里面的个数要与之匹配,否则会抛出如下错误:

可是若是一个 List 中存在多个不一样类型的对象时,咱们可使用这个方法:

toJSONBytes(Object o)

JSON对象转换成Byte(字节)数组

咱们平时在进行网络通信的时候,须要将对象转为字节而后进行传输。咱们使用字符串的时候,不禁然的能够想到字符串中有个很便捷的 API 能够将字符串转为字节数组

String str = "小菜";
byte[] bytes = str.getBytes();

可是咱们若是要将一个 JavaBean 对象转为字节数组的时候,咱们得借助ByteArrayOutputStream 流的帮助:

图片

这种方式也能够很好的将 JavaBean 对象转为字节数组,可是代码难免有点多了!而FastJSON 中也提供了很方便的 API 以供使用:

图片

而咱们要将字节数组转为对象,FastJSON 也一样支持:

图片

parseObject()这个方法中咱们又看到了一个奇怪的参数 Feature,咱们点击进入源码能够发现这其实也是一个枚举类:

图片

看的一样云里雾里的,这么多对象实例,如下咱们对比较经常使用的作出了注释:

对象 描述
AllowUnQuotedFieldNames 决定parser是否将容许使用非双引号属性名
AllowSingleQuotes 决定parser是否容许单引号来包住属性名称和字符串值
InternFieldNames 决定JSON对象属性名称是否能够被String#intern 规范化表示,若是容许,则JSON全部的属性名将会 intern() ;若是不设置,则不会规范化,默认下,该属性是开放的。
AllowISO8601DateFormat 设置为true则遇到字符串符合ISO8601格式的日期时,会直接转换成日期类
AllowArbitraryCommas 容许多重逗号,若是设为true,则遇到多个逗号会直接跳过
UseBigDecimal 设置为true则用BigDecimal类来装载数字,不然用的是double
IgnoreNotMatch 忽略不匹配
DisableCircularReferenceDetect 禁用循环引用检测
InitStringFieldAsEmpty 对于没有值得字符串属性设置为空串
SupportArrayToBean 支持数组to对象
OrderedField 属性保持原来的顺序
DisableSpecialKeyDetect 禁用特殊字符检查
UseObjectArray 使用对象数组
writeJSONString(OutputStream os, Object o)

这个方法是将对象写入输出流中的:

图片

传入的参数还能够是一个 Writer 对象:

图片

以上即是咱们平时经常使用的 API,除此以外,在 FastJSON 中还有一个注解@JSONField 咱们也要学会灵活运用

@JSONField
命名重塑

图片

注: 若属性是 私有的,必需要有 set() 方法,不然没法反序列化!

@JSONField 用法简单,能够配置在 getter() 、setter() 或者 属性字段 上

图片

测试结果:

图片

这个方法的最大好处即是用来对接奇奇怪怪的文档,为何说奇奇怪怪呢,有时候咱们须要调用第三方的接口,可是这个接口返回的值多是不符合命名规范的,那咱们这边就须要定义一个实体类去接收它(Map虽然也行,可是也不规范)。

这个时候咱们定义的实体类的属性名就得按照返回的字段名来命名,这对强迫症程序猿来讲是致命打击,这个时候 @JSONField 的用处就来了,咱们简单看个例子。有个车牌信息实体的返回字段是这样的:

{"PLATEID" : 01"PLATE": '闽A6666D', "IMAGEURL":'http://...'}

咱们能够看到返回的字段名全都不知足小驼峰规则,咱们定义的实体类可不能这样,借助 @JSONField 的写法以下:

图片

测试下是否可以成功接收结果:

图片

能够看到咱们已经成功接收到结果了,并且实体类的命名也符合咱们的规范,一箭双雕。

图片

DataFormat

咱们也可使用该注解来将咱们的日期格式化成咱们想要的样子

图片

控制序列化

在序列化或反序列化的时候咱们能够指定字段不序列化,这个有点像 Java 流中的 transient 修饰。FastJSON 中也能够实现类似的功能:

图片

可是反序列化有个缺点就是,虽然值是空的,可是属性名还在~

ordinal

咱们可使用ordinal来指定字段的顺序

图片

经过接收结果能够看到 属性字段 按照咱们规定的顺序所排列,用处能够在于咱们返回字段给前端过多的时候,将有用的字段优先排列到前面,能够更好的取值,而不用一层一层的查找须要的字段。

定制序列化

万物皆可定制,序列化也不例外~ 咱们可使用serializeUsing制定属性的序列化类

图片

经过这种方式咱们针对 age 这个属性进行了处理,给指定字段加上了单位.