JSON解析

JSON
定义
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
作用
数据标记,存储,传输
特点
1. 读写速度快
2. 解析简单
3. 轻量级
4. 独立于语言,平台
5. 具有自我描叙性
JSON 解析
语法
JSON 建构于两种结构:
名称 / 对的集合( A collection of name/value pairs )。不同的语言中,它被理解为对象
object ),纪录( record ),结构( struct ),字典( dictionary ),哈希表( hash table ),
有键列表( keyed list ),或者关联数组 ( associative array )。
值的有序列表( An ordered list of values )。在大部分语言中,它被理解为数组( array )。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式
在同样基于这些结构的编程语言之间交换成为可能。
JSON 具有以下这些形式:
对象是一个无序的 “‘ 名称 / 集合。一个对象以 “{” (左括号)开始, “}” (右括号)结束。每个 名称
跟一个 “:” (冒号); “‘ 名称 / 之间使用 “,” (逗号)分隔。
 

数组是值( value )的有序集合。一个数组以 “[” (左中括号)开始, “]” (右中括号)结束。值之间使用
“,” (逗号)分隔。
值( value )可以是双引号括起来的字符串( string )、数值 (number) true false null 、对象
object )或者数组( array )。这些结构可以嵌套。
字符串( string )是由双引号包围的任意数量 Unicode 字符的集合,使用反斜线转义。一个字符
character )即一个单独的字符串( character string )。
字符串( string )与 C 或者 Java 的字符串非常相似。
 
数值( number )也与 C 或者 Java 的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编
码细节。
JSON 解析方式
Android Studio 自带 org.json 解析
  • 解析原理:基于文档驱动,需要把全部文件读入到内存中,然后遍历所有数据,根据需要检索想要
    的数据
  • 具体使用

 

Gson 解析
解析原理:基于事件驱动
解析流程:根据所需取的数据 建立 1 个对应于 JSON 数据的 JavaBean 类,即可通过简单操作解析出
所需数据
Gson 不要求 JavaBean 类里面的属性一定全部和 JSON 数据里的所有 key 相同,可以按需取数据
具体实现
转化成 JavaBean
Jackson 解析
  • 解析原理:基于事件驱动
  • 解析过程:
  1. 类似 GSON ,先创建 1 个对应于 JSON 数据的 JavaBean 类,再通过简单操作即可解析
  2. Gson 解析不同的是: GSON 可按需解析,即创建的 JavaBean 类不一定完全涵盖所要解析
    JSON 数据,按需创建属性;但 Jackson 解析对应的 JavaBean 必须把 Json 数据里面的所有 key
    都有所对应,即必须把 JSON 内的数据所有解析出来,无法按需解析

     

 导入Jackson依赖

使用 Jackson 解析
Fastjson 解析
导入 Fastjson 依赖
implementation 'com.alibaba:fastjson:1.2.57'
使用 Fastjson 解析
自定义一个 JSON 解析库
编写一个 JSON 解析器实际上就是一个方法,它的输入是一个表示 JSON 的字符串,输出是结构化的对应
到语言本身的数据结构
一般来说,解析过程包括词法分析和语法分析两个阶段。词法分析阶段的目标是按照构词规则将 JSON
字符串解析成 Token 流,比如有如下的 JSON 字符串:
 
{
"key" : "value" ,
}
结果词法分析后,得到一组 Token ,如下:
{key : value, }
词法分析解析出 Token 序列后,接下来要进行语法分析。语法分析的目的是根据 JSON 文法检查上面
Token 序列所构成的 JSON 结构是否合法。比如 JSON 文法要求非空 JSON 对象以键值对的形式出现,
形如 object = {string : value} 。如果传入了一个格式错误的字符串,比如
{
"key" , "value"
}
那么在语法分析阶段,语法分析器分析完 Token name 后,认为它是一个符合规则的 Token ,并且认为
它是一个键。接下来,语法分析器读取下一个 Token ,期望这个 Token : 。但当它读取了这个
Token 发现这个 Token , ,并非其期望的 : ,于是文法分析器就会报错误。
JSON 解析分析小结
1. 通过词法分析是将字符串解析成一组 Token 序列
2. 然后通过语法分析检查输入的 Token 序列所构成的 JSON 格式是否合法
词法分析
按照 构词规则 JSON 字符串解析成 Token 流。请注意双引号引起来词 构词规则,所谓构词规则是
指词法分析模块在将字符串解析成 Token 时所参考的规则。在 JSON 中,构词规则对应于几种数据类
型,当词法解析器读入某个词,且这个词类型符合 JSON 所规定的数据类型时,词法分析器认为这个词
符合构词规则,就会生成相应的 Token 。这里我们可以参考 http://www.json.org/ JSON 的定义,罗
列一下 JSON 所规定的数据类型:
  • BEGIN_OBJECT{
  • END_OBJECT}
  • BEGIN_ARRAY[
  • END_ARRAY]
  • NULLnull
  • NUMBER(数字)
  • STRING(字符串)
  • BOOLEANtrue/false
  • SEP_COLON:
  • SEP_COMMA,
当词法分析器读取的词是上面类型中的一种时,即可将其解析成一个 Token 。我们可以定义一个枚举类
来表示上面的数据类型,如下:
在解析过程中,仅有 TokenType 类型还不行。我们除了要将某个词的类型保存起来,还需要保存这个
词的字面量。所以,所以这里还需要定义一个 Token 类。用于封装词类型和字面量,如下:
定义好了 Token 类,接下来再来定义一个读取字符串的类
有了 TokenType Token CharReader 这三个辅助类,接下来我们就可以实现词法解析器了
上面的代码是词法分析器的实现,部分代码这里没有贴出来,后面具体分析的时候再贴。先来看看词法
分析器的核心方法 start ,这个方法代码量不多,并不复杂。其通过一个死循环不停的读取字符,然后
再根据字符的类型,执行不同的解析逻辑。上面说过, JSON 的解析过程比较简单。原因在于,在解析
时,只需通过每个词第一个字符即可判断出这个词的 Token Type 。比如:
  • 第一个字符是{}[],:,直接封装成相应的 Token 返回即可
  • 第一个字符是n,期望这个词是nullToken 类型是NULL
  • 第一个字符是tf,期望这个词是true或者falseToken 类型是 BOOLEAN
  • 第一个字符是,期望这个词是字符串,Token 类型为String
  • 第一个字符是0~9-,期望这个词是数字,类型为NUMBER
    正如上面所说,词法分析器只需要根据每个词的第一个字符,即可知道接下来它所期望读取的到的
    内容是什么样的。如果满足期望了,则返回 Token ,否则返回错误。下面就来看看词法解析器在
    碰到第一个字符是 n 时的处理过程。先看碰到字符 n 的处理过程:

上面的代码很简单,词法分析器在读取字符 n 后,期望后面的三个字符分别是 u,l,l ,与 n 组成词 null
如果满足期望,则返回类型为 NULL Token ,否则报异常。 readNull 方法逻辑很简单,不多说了。
接下来看看 string 类型的数据处理过程:
string 类型的数据解析起来要稍微复杂一些,主要是需要处理一些特殊类型的字符。 JSON 所允许的特
殊类型的字符如下:
最后一种特殊字符 \/ 代码中未做处理,其他字符均做了判断,判断逻辑在 isEscape 方法中。在传入
JSON 字符串中,仅允许字符串包含上面所列的转义字符。如果乱传转义字符,解析时会报错。对于
STRING 类型的词,解析过程始于字符 ,也终于 。所以在解析的过程中,当再次遇到字符
readString 方法会认为本次的字符串解析过程结束,并返回相应类型的 Token
上面说了 null 类型和 string 类型的数据解析过程,过程并不复杂,理解起来应该不难。至于 boolean
number 类型的数据解析过程,大家有兴趣的话可以自己看源码,这里就不在说了。
语法分析
当词法分析结束后,且分析过程中没有抛出错误,那么接下来就可以进行语法分析了。语法分析过程以
词法分析阶段解析出的 Token 序列作为输入,输出 JSON Object JSON Array 。语法分析器的实现的
文法如下:
当词法分析结束后,且分析过程中没有抛出错误,那么接下来就可以进行语法分析了。语法分析过程以
词法分析阶段解析出的 Token 序列作为输入,输出 JSON Object JSON Array 。语法分析器的实现的
文法如下:
 

语法分析器的实现需要借助两个辅助类,也就是语法分析器的输出类,分别是 JsonObject
JsonArray 。代码如下:
语法解析器的核心逻辑封装在了 parseJsonObject parseJsonArray 两个方法中,接下来我会详细分
parseJsonObject 方法, parseJsonArray 方法大家自己分析吧。 parseJsonObject 方法实现如下:
 
parseJsonObject 方法解析流程大致如下:
1. 读取一个 Token ,检查这个 Token 是否是其所期望的类型
2. 如果是,更新期望的 Token 类型。否则,抛出异常,并退出
3. 重复步骤 1 2 ,直至所有的 Token 都解析完,或出现异常
上面的步骤并不复杂,但有可能不好理解。这里举个例子说明一下,有如下的 Token 序列:
{ id : 1 }
parseJsonObject 解析完 { Token 后,接下来它将期待 STRING 类型的 Token 或者 END_OBJECT 类型
Token 出现。于是 parseJsonObject 读取了一个新的 Token ,发现这个 Token 的类型是 STRING
型,满足期望。于是 parseJsonObject 更新期望 Token 类型为 SEL_COLON ,即 : 。如此循环下去,直至
Token 序列解析结束或者抛出异常退出。
上面的解析流程虽然不是很复杂,但在具体实现的过程中,还是需要注意一些细节问题。比如:
1. JSON 中,字符串既可以作为键,也可以作为值。作为键时,语法分析器期待下一个 Token
型为 SEP_COLON 。而作为值时,则期待下一个 Token 类型为 SEP_COMMA END_OBJECT 。所
以这里要判断该字符串是作为键还是作为值,判断方法也比较简单,即判断上一个 Token 的类型
即可。如果上一个 Token SEP_COLON ,即 : ,那么此处的字符串只能作为值了。否则,则只能
做为键。
2. 对于整数类型的 Token 进行解析时,简单点处理,可以直接将该整数解析成 Long 类型。但考虑
到空间占用问题,对于 [Integer.MIN_VALUE, Integer.MAX_VALUE] 范围内的整数来说,解析成
Integer 更为合适,所以解析的过程中也需要注意一下。
参考
https://segmentfault.com/a/1190000010998941#articleHeader1
Gson 原理解析
在这个序列化和反序列化的过程中,
充当的了一个解析器的角色
JsonElement
该类是一个抽象类,代表着 json 串的某一个元素。这个元素可以是一个 Json(JsonObject) 、可以是一个
数组 (JsonArray) 、可以是一个 Java 的基本类型 (JsonPrimitive) 、当然也可以为
null(JsonNull);JsonObject,JsonArray,JsonPrimitive JsonNull 都是 JsonElement 这个抽象类的子类。
JsonElement 提供了一系列的方法来判断当前的 JsonElement
各个 JsonElement 的关系可以用如下图表示:
JsonObject 对象可以看成 name/values 的集合,而这写 values 就是一个个 JsonElement, 他们的结构可以
用如下图表示:
TypeAdapter 的工作原理
Gson 的整体解析原理
Gson 的反射解析机制
Gson 解析常见的错误
Expected BEGIN_ARRAY but was STRING at line 1 column 27
这种错误一般都是原来是一个字段需要是数组类型,但是事实上给的是 ””, 导致的
- 解决办法
1. 让返回 null 即可解决问题
2. Gson 自带的解决方案
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值