什么是 JSON
JSON(JavaScript Object Notation)是一种轻量的数据格式,它不是一门编程语言。JSON是基于JavaScript Programming Language,Standard ECMA-262 3rd Edition - December 1999的一个子集。但 JSON
并不属于 JavaScript
,很多编程语言都有针对 JSON
的解析器和序列化器。
JSON 语法
根据红宝书中的介绍,JSON 有三种类型的值,分别为简单值、对象和数组。
- 简单值:使用与
JavaScript
相同的语法,可以在JSON
中表示字符串、数值、布尔值和null
。但JSON
不支持JavaScript
中的特殊值undefined
- 对象:对象作为一种复杂数据类型,表示的是一组无序的键值对儿。而每个键值对儿中的值可 以是简单值,也可以是复杂数据类型的值。
- 数组:数组也是一种复杂数据类型,表示一组有序的值的列表,可以通过数值索引来访问其中 的值。数组的值也可以是任意类型——简单值、对象或数组。
在 JavaScript 中已经内置了 JSON
对象,它有 parse
和 stringify
两个方法。
在实际工作中,这两个方法我们也经常用到,例如实现对象深拷贝时
const obj = {
name: 'Jay',
age: 41
}
const jsonStr = JSON.stringify(obj) // {"name":"Jay","age":41}
const copiedObj = JSON.parse(jsonStr) // {name: "Jay", age: 41}
本文就来实现 JSON
的 parse
方法。
实现 parse
我们先看一下 JSON
字符串的结构
const json = `
{
"status": 100,
"msg": "返回成功",
"data": {
"string": "abc",
"array": [1,2,3],
"children": [
{ "name": "Jay", "age": 41, "occupation": "Musician"},
{ "name": "Jack", "age": 56, "occupation": "CEO"},
{ "name": "Kobe", "age": 42, "occupation": "Basketball players"}
]
}
}
`;
我们的函数就叫做 fakeParseJSON
,我们先用原生方法跑一下
const fakeParseJSON = JSON.parse
fakeParseJSON(json) // {status: 100, msg: "返回成功", data: {…}}
我们先从简单值开始来写
parseValue
值(value)可以是双引号括起来的字符串(string)、数值(number)、true
、false
、null
、对象(object)或者数组(array)。这些结构可以嵌套。
流程图如下:
以值为 string
类型为例
const str = `
"hello world"
`
上面就是一个简单的 JSON
值(value
),根据流程图,从左往右会经过 whitespace
, "
, string
, "
, whitespace
。我们就一个一个来处理。
function fakeParseJSON(str) {
let i = 0
// 处理 whitespace, 遇到空格,回车,制表符等直接跳过
function parseWhiteSpace() {
while(str[i] === ' ' || str[i] === '\n' || str[i] === '\r' || str[i] === '\t') {
i++
}
}
function parseValue() {
// 首先处理前面可能有的空格
parseWhiteSpace()
// 处理 string
if(str[i] === '"') { // 以双引号开头
i++
let res = ''
while(str[i] !== '"') {
res += str[i]
i++
}
// 继续往下移
i++
return res
}
}
}
测试一下
fakeParseJSON(str) // hello world
我们的 JSON
值的类型不仅有 string
,还有 number
, object
等类型。我们最后要处理的都是 JSON
的 value
,而且我们知道 value
是 object
类型是“名称/值”对的集合形式,名称一般都是字符串,值的话各种类型都有。在解析 JSON 对象时,我们要处理名称,这里我们先单独抽离一个专门处理字符串的函数 parseString
,我们改动一下代码
function fakeParseJSON(str) {
let i = 0
// 处理 whitespace,
// ...
// 处理字符串
function parseString() {
parseWhiteSpace()
if(str[i] === '"') { // 以双引号开头
i++
let res = ''
while(str[i] !== '"') {
res += str[i]
i++
}
// 继续往下移
i++
return res
}
}
// 处理结果
function parseValue() {
return parseString()
}
// 输出结果
return parseValue()
}
parseObject
接下来我们来处理 JSON
对象,先看看流程图
从图中我们可以看出是以"{
" 开头,"}
"结尾,中间可能会经历 whitespace
, string
, :
, whitespace
, value
, ,
, ...
。我们也先以值为简单类型的为例
const obj = `
{
"msg": "返回成功"
}
`
我们也加上一个 parseObject
的函数
function fakeParseJSON(str) {
let i = 0
// 处理 whitespace
// ...
// 处理冒号
function parseColon() {
if(str[i] !== ":") {
throw new Error('Expected ":".')
}
i++
}
// 处理字符串
// ...
// 处理对象
function parseObject() {
parseWhiteSpace()
if(str[i] === '{') {
i++
parseWhiteSpace()
const result = {}
while(str[i] !== '}') {
// 处理字符串
const key = parseString()
parseWhiteSpace()
parseColon() // 这里新加一个处理冒号的
const value = parseValue()
result[key] = value
}
i++
return result
}
}
function parseValue() {
parseWhiteSpace()
const value =
parseString() ||
parseObject()
parseWhiteSpace()
return value
}
return parseValue()
}
测试一下
fakeParseJSON(obj) // {msg: "返回成功"}
parseArray
我们继续丰富一下,在之前的 json
对象基础上增加数组类型
const obj = `
{
"msg": "返回成功",
"arr": ["a","b","c"]
}
`;
这里我们有两个任务要处理,其一是处理新增的 ,
,还有就是处理数组。
function fakeParseJSON(str) {
let i = 0
// ...
// 处理逗号
function parseComma() {
if (str[i] !== ",") {
throw new Error('Expected ",".');
}
i++;
}
// 处理对象
function parseObject() {
parseWhiteSpace()
if(str[i] === '{') {
// ...
let initial = true
while(str[i] !== '}') {
if(!initial) {
parseComma() // 处理逗号
parseWhiteSpace()
}
// ...
initial = false
}
// ...
}
}
// ...
}
其二是处理数组,我们看看数组的流程图
和处理对象类型差不多,直接上代码
function fakeParseJSON(str) {
let i = 0
// ...
// 处理数组
function parseArray() {
if(str[i] === "[") {
i++
parseWhiteSpace()
const result = []
let initial = true
while(str[i] !== "]") {
if(!initial) {
parseComma()
parseWhiteSpace()
}
const value = parseValue()
result.push(value)
initial = false
}
i++
return result
}
}
function parseValue() {
parseWhiteSpace()
const value =
parseString() ||
parseObject() ||
parseArray()
parseWhiteSpace()
return value
}
return parseValue()
}
parseNumber
处理 number
情况比较麻烦,我们也是先看一下流程图
从图中可以看出,需要处理负数,小数以及指数等情况
function fakeParseJSON(str) {
let i = 0
// ...
// 处理 number
function parseNumber() {
let start = i
if (str[i] === "-") i++
if (str[i] === "0") {
i++
} else if (str[i] >= "1" && str[i] <= "9") {
i++
while (str[i] >= "0" && str[i] <= "9") {
i++;
}
}
if (str[i] === ".") {
i++
while (str[i] >= "0" && str[i] <= "9") {
i++
}
}
if (str[i] === "e" || str[i] === "E") {
i++
if (str[i] === "-" || str[i] === "+") {
i++
}
while (str[i] >= "0" && str[i] <= "9") {
i++
}
}
if (i > start) {
return Number(str.slice(start, i));
}
}
function parseValue() {
parseWhiteSpace()
const value =
parseString() ||
parseObject() ||
parseArray() ||
parseNumber()
parseWhiteSpace()
return value
}
// 输出结果
return parseValue()
}
其他情况
我们还差布尔值以及 null
的情况
function fakeParseJSON(str) {
let i = 0
// ...
// true, false and null
function parseKeyword(name, value) {
if (str.slice(i, i + name.length) === name) {
i += name.length;
return value;
}
}
function parseValue() {
parseWhiteSpace()
const value =
parseString() ||
parseObject() ||
parseArray() ||
parseNumber() ||
parseKeyword("true", true) ||
parseKeyword("false", false) ||
parseKeyword("null", null)
parseWhiteSpace()
return value
}
// 输出结果
return parseValue()
}
结语
至此,我们大概实现了一个 JSON.parse
方法,当然还很不完善,比如字符串的处理以及容错处理。
字符串(_string_)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。