JSON语法能够表示如下三种类型的值:javascript
null
, 可是不支持undefined
JSON不支持变量/函数/对象实例java
字符串编程
"Hello JSON"
数字json
66
布尔值数组
true
null数据结构
null
和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时必定要注意)this
对象没有变量声明, 由于JSON根本就没有变量的概念(它不是一个编程语言)code
末尾没有分号
{ "foo": "bar", "num": 66, "status": true }
和JavaScript类似, 对象能够嵌套对象
{ "foo": "bar", "num": 66, "status": true, "baz": { "num": 88 } }
注, 同名属性能够在不一样的对象中, 可是不能出如今同一个对象中
和JavaScript对比来看 ->
let arr = ['hello', 66, true];
一样没有变量声明和结尾的分号, 同时注意字符串简单值要加双引号
["hello", 66, true]
数组和对象结合起来能够构成复杂的集合, 好比students.json文件中多是这样婶儿的
[ { "name": "小明", "age": 10, "score": { "math": 88, "english": 99 } }, { "name": "小强", "age": 11, "score": { "math": 98, "english": 96 } } ]
看到以上同JavaScript的不一样之处, 咱们能够知道为何说JSON是JavaScript的一个严格子集了吧
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
几乎同样.
注意, 为何说是几乎同样呢?
这就提供了一种克隆对象和数组的方法😆
undefined
的属性, 那么在序列化的过程当中它会被筛掉;undefined
的项, 那么在序列化过程当中undefined
的项会变成null
;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()
方法分别用来实现上述两项功能. 这两个方法都有一些选项, 经过它们能够改变过滤的方式, 或者改变序列化的过程.