乐趣区

5分钟了解JSON那些事儿

JSON
简介

JSON 是 JavaScript Object Notation(JavaScript 对象表示法)的缩写
JSON 是一种数据格式, 而不是一种编程语言, 用来表示结构化数据
JSON 是 JavaScript 的一个严格子集
JSON 并不从属于 JavaScript, 很多编程语言都可以用 JSON 数据格式

语法
JSON 语法可以表示以下三种类型的值:

简单值: 字符串 / 数值 / 布尔值 /null, 但是不支持 undefined

对象
数组

JSON 不支持变量 / 函数 / 对象实例
简单值
字符串
“Hello JSON”
数字
66
布尔值
true
null
null
对象
和 JavaScript 对比来看 ->
JavaScript 表示对象
键值对的键可以加引号也可以不加, 如果加引号, 可以加单引号也可以加双引号
// 最常见就这么写
const obj1 = {
foo: ‘bar’,
num: 66,
status: true
};
// 这么写也 oconstet obj2 = {
‘foo’: ‘bar’,
‘num’: 66,
‘status’: true
};
// 这么写也 ok
const obj1 = {
“foo”: “bar”,
“num”: 66,
“status”: true
};
JSON 表示对象
键值对的键必须加双引号(手写 JSON 时一定要注意)
对象没有变量声明, 因为 JSON 根本就没有变量的概念(它不是一个编程语言)
末尾没有分号
{
“foo”: “bar”,
“num”: 66,
“status”: true
}
和 JavaScript 相似, 对象可以嵌套对象
{
“foo”: “bar”,
“num”: 66,
“status”: true,
“baz”: {
“num”: 88
}
}
注, 同名属性可以在不同的对象中, 但是不能出现在同一个对象中
数组
和 JavaScript 对比来看 ->
JavaScript 表示数组
let arr = [‘hello’, 66, true];
JSON 表示数组
同样没有变量声明和结尾的分号, 同时注意字符串简单值要加双引号
[“hello”, 66, true]
数组和对象结合起来可以构成复杂的集合, 比如 students.json 文件中可能是这样婶儿的
[
{
“name”: “ 小明 ”,
“age”: 10,
“score”: {
“math”: 88,
“english”: 99
}
},
{
“name”: “ 小强 ”,
“age”: 11,
“score”: {
“math”: 98,
“english”: 96
}
}
]
看到以上同 JavaScript 的不同之处, 我们可以知道为什么说 JSON 是 JavaScript 的一个严格子集了吧
JSON 序列化与解析
基本用法
ECMAScript5 定义了全局对象 JSON, 用来解析 JSON 字符串
简单来说, JSON 对象有两个方法

JSON.stringify(): 把 JavaScript 对象序列化为 JSON 字符串

JSON.parse(): 把 JSON 字符串解析为原生 JavaScript 值

const book = {
name: ‘Learn JavaScript in One Day’,
pages: 1
};

const jsonText = JSON.stringify(book); // 序列化
// “{“name”:”Learn JavaScript in One Day”,”pages”:1}”

const parseText = JSON.parse(jsonText); // 解析
// {name: “Learn JavaScript in One Day”, pages: 1}
默认情况下, JSON.stringify()输出的 JSON 字符串不包含任何空格字符或缩进(是不是给我们提供了一种将去除数据中无用的空白和缩进的方法呢).
在序列化 JavaScript 对象时, 所有函数及原型成员都会被有意忽略, 不体现在结果中. 此外, 值为 undefined 的任何属性也都会被跳过. 结果中最终都是值为有效 JSON 数据类型的实例属性.
const book = {
name: ‘Learn JavaScript in One Day’,
pages: 1,
foo: undefined
};

const jsonText = JSON.stringify(book); // 序列化
// “{“name”:”Learn JavaScript in One Day”,”pages”:1}”
// foo 的值为 undefined, 所被忽略掉了
将 JSON 字符串直接传递给 JSON.parse()就可以得到相应的 JavaScript 值.
JSON.parse()就是 JSON.stringify()的逆向操作. 将一个 JavaScript 值序列化之后再解析 JSON.parse(JSON.stringify(foo))和原来的 foo 几乎一样.
注意, 为什么说是几乎一样呢?

如果 foo 是一个对象或数组, 那么这么一折腾就变成了两个不同的对象或数组了; 这就提供了一种克隆对象和数组的方法????

如果 foo 是一个对象, 并且 foo 中有值为 undefined 的属性, 那么在序列化的过程中它会被筛掉;
如果 foo 是一个数组, 并且其中有 undefined 的项, 那么在序列化过程中 undefined 的项会变成 null;
如果 foo 是一个简单值(数字 / 布尔值 / 字符串), 那么这么一折腾结果完全是相等的.

const foo1 = {a: 1};
JSON.parse(JSON.stringify(foo1)) === foo1; // false
const foo2 = [1, 2];
JSON.parse(JSON.stringify(foo2)) === foo2; // false
const foo3 = {a: 1, b: undefined};
JSON.parse(JSON.stringify(foo3)); // {a: 1}
const foo4 = [1, true, undefined];
JSON.parse(JSON.stringify(foo4)); // [1, true, null]
const foo5 = true;
JSON.parse(JSON.stringify(foo5)) === foo5; // true
如果传给 JSON.parse()的字符串不是有效的 JSON, 该方法会抛出错误.
进阶用法
序列化选项
JSON.stringify()除了要序列化的 JavaScript 对象外, 还可以接收另外两个参数, 这两个参数用于指定以不同的方式序列化 JavaScript 对象.
第一个参数是个过滤器, 可以是一个数组, 也可以是一个函数;
第二个参数是一个选项, 表示是否在 JSON 字符串中保留缩进.
1. 过滤结果
如果过滤器参数是数组, 那么 JSON.stringify()的结果中将只包含数组中列出的属性
const book = {
name: ‘Learn JavaScript in One Day’,
pages: 1
};
JSON.stringify(book, [‘name’]);
// “{“name”:”Learn JavaScript in One Day”}”
再看下面的例子????:
const stu = {
name: “ 小明 ”,
age: 10,
score: {
math: 88,
english: 99
}
};
JSON.stringify(stu, [‘name’, ‘score’]);
// “{“name”:” 小明 ”,”score”:{}}”
注意, 最后结果中 score 是一个空对象, 那么小明的成绩哪儿去了?
好像我们的目的是要显示 name 和 score 属性, 只去掉 age 就行了, 但是 score 对象中咋啥也没有了?
那我们把 stu 对象重新改改试试:
var stu = {
name: “ 小明 ”,
age: 10,
score: {
math: 88,
english: 99,
name: ‘xiuxiu~’,
score: 100
}
};
JSON.stringify(stu, [‘name’, ‘score’]);
// “{“name”:” 小明 ”,”score”:{“name”:”xiuxiu~”,”score”:100}}”
这下看明白了么, 原来这个过滤是对每一层级的对象都过滤一遍.
如果第二个参数是函数, 行为会稍有不同. 传入的函数接收两个参数, 属性名 (键) 和属性值(值). 根据属性名可以知道应该如何处理要序列化的对象中的属性. 属性名只能是字符串, 而在值并非键值对儿结构的值时, 键名可以是空字符串. 函数返回的值就是相应键的值, 如果函数返回了 undefined, 那么相应的属性会被忽略.
const stu = {
name: “ 小明 ”,
age: 10,
score: {
math: 88,
english: 99
}
};
const newStu = JSON.stringify(stu, (key, value) => {
switch(key) {
case ‘math’:
return 100;
case ‘english’:
return 100;
default:
return value;
}
});
// “{“name”:” 小明 ”,”age”:10,”score”:{“math”:100,”english”:100}}”
// 成功将小明的成绩改成了 100 分, 哈哈哈~
2. 字符串缩进
JSON.stringify()方法的第三个参数用于控制结果中的缩进和空白符. 如果这个参数是一个数值, 那它表示的是每个级别缩进的空格数.
JSON.stringify()会在结果字符串中插入换行符以提高可读性.
最大缩进空格数为 10, 所有大于 10 的值都会自动转换为 10.
const stu = {
name: “ 小明 ”,
age: 10,
score: {
math: 88,
english: 99
}
};
JSON.stringify(stu, null, 4); // 4 个空格的缩进
/* 序列化后的结果
“{
“name”: “ 小明 ”,
“age”: 10,
“score”: {
“math”: 88,
“english”: 99
}
}”
*/
如果缩进参数是一个字符串而非数值, 则这个字符串将在 JSON 字符串中被用作缩进字符(不再使用空格).
缩进字符串最长不能超过 10 个字符长. 如果字符串长度超过了 10 个, 结果中将只出现前 10 个字符.
JSON.stringify(stu, null, ‘–‘);
/* 序列化后的结果
“{
–“name”: “ 小明 ”,
–“age”: 10,
–“score”: {
—-“math”: 88,
—-“english”: 99
–}
}”
*/
3.toJSON
有时候, JSON.stringify()还是不能满足对某些对象进行自定义序列化的需求. 在这些情况下, 可以给对象定义 toJSON()方法, 返回其自身的 JSON 数据格式. 原生 Date 对象有一个 toJSON()方法, 能够将 JavaScript 的 Date 对象自动转换成 ISO8601 日期字符串 (与在 Date 对象上调用 toISOString() 的结果完全一样).
JSON.stringify(new Date());
// 序列化后的结果: “”2019-04-08T11:31:05.778Z””
new Date().toJSON();
new Date().toISOString();
// 直接调用 toJSON 和 toISOString 方法同样能得到字符串: “2019-04-08T11:31:44.432Z”
可以让 toJSON()方法返回任何值, 它都能正常工作.
const stu = {
name: “ 小明 ”,
age: 10,
score: {
math: 88,
english: 99
},
toJSON() {
return this.name;
}
};
JSON.stringify(stu);
// 序列化后的结果: “” 小明 ””
toJSON()可以作为函数过滤器的补充, 因此理解序列化的内部顺序十分重要. 假设把一个对象传入 JSON.stringify(), 序列化该对象的顺序如下:
(1) 如果存在 toJSON()方法而且能通过它取得有效的值, 则调用该方法. 否则, 返回对象本身;
(2) 如果提供了第二个参数, 应用这个函数过滤器. 传入函数过滤器的值是第 (1) 步返回的值;
(3) 对第 (2) 步返回的每个值进行相应的序列化;
(4) 如果提供了第三个参数, 执行相应的格式化.
解析选项
JSON.parse()方法也可以接收另一个参数, 该参数是一个函数, 将在每个键值对儿上调用. 为了区别 JSON.stringify()接收的替换 (过滤) 函数(replacer), 这个函数被称为还原函数(reviver), 但实际上这两个函数的签名是相同的——它们都接收两个参数, 一个键和一个值, 而且都需要返回一个值.
如果还原函数返回 undefined, 则表示要从结果中删除相应的键; 如果返回其他值, 则将该值插入到结果中.
const stu = {
name: “ 小明 ”,
age: 10,
score: {
math: 88,
english: 99
}
};
const jsonText = JSON.stringify(stu);
JSON.parse(jsonText, (key, value) => {
if (key === ‘name’) {
return value + ‘ 牛逼了!’;
} else {
return value;
}
});
/* 解析结果
{
age: 10
name: “ 小明牛逼了!”
score: {math: 88, english: 99}
}
*/
小结
JSON 是一个轻量级的数据格式, 可以简化表示复杂数据结构的工作量. JSON 使用 JavaScript 语法的子集表示对象、数组、字符串、数值、布尔值和 null.
ECMAScript5 定义了一个原生的 JSON 对象, 可以用来将对象序列化为 JSON 字符串或者将 JSON 数据解析为 JavaScript 对象. JSON.stringify()和 JSON.parse()方法分别用来实现上述两项功能. 这两个方法都有一些选项, 通过它们可以改变过滤的方式, 或者改变序列化的过程.

退出移动版