23.1 语法
JSON语法支持表示3种类型的值:
❑ 简单值:字符串、数值、布尔值和null可以在JSON中出现,就像在JavaScript中一样。特殊值undefined不可以。
❑ 对象:第一种复杂数据类型,对象表示有序键/值对。每个值可以是简单值,也可以是复杂类型。
❑ 数组:第二种复杂数据类型,数组表示可以通过数值索引访问的值的有序列表。数组的值可以是任意类型,包括简单值、对象,甚至其他数组。
JSON没有变量、函数或对象实例的概念。JSON的所有记号都只为表示结构化数据,虽然它借用了JavaScript的语法,但是千万不要把它跟JavaScript语言混淆。
23.1.1 简单值
最简单的JSON可以是一个数值,字符串等
JavaScript字符串与JSON字符串的主要区别是:
JSON字符串必须使用双引号(单引号会导致语法错误)。
布尔值和null本身也是有效的JSON值。
不过,实践中更多使用JSON表示比较复杂的数据结构,其中会包含简单值。
23.1.2 对象
对象使用与JavaScript对象字面量略为不同的方式表示。
以下是JavaScript中的对象字面量:
let person = {name: "Amy",age: 29};
虽然这对JavaScript开发者来说是标准的对象字面量,但JSON中的对象必须使用双引号把属性名包围起来。
下面的代码与前面的代码是一样的:
let person = {"name": "Amy","age": 29};
而用JSON表示相同的对象的语法是:
{"name": "Amy","age": 29}
与JavaScript对象字面量相比,JSON主要有两处不同:
首先,没有变量声明(JSON中没有变量);
其次,最后没有分号(不需要,因为不是JavaScript语句)。
同样,用引号将属性名包围起来才是有效的JSON。
属性的值可以是简单值或复杂数据类型值,后者可以在对象中再嵌入对象。
23.1.3 数组
JSON的第二种复杂数据类型是数组。
数组在JSON中使用JavaScript的数组字面量形式表示。
例如,以下是一个JavaScript数组:
let values = [25, '你好', true];
在JSON中可以使用类似语法表示相同的数组:
[25, '你好', true]
23.2 解析与序列化
JSON的迅速流行很大程度上因为JSON可以直接被解析成可用的JavaScript对象。
JSON出现之后就迅速成为了Web服务的事实序列化标准。
23.2.1 JSON对象
早期的JSON解析器基本上就相当于JavaScript的eval()函数。
因为JSON是JavaScript语法的子集,所以eval()可以解析、解释,并将其作为JavaScript对象和数组返回。
JSON对象有两个方法:stringify()和parse()。
这两个方法分别可以将JavaScript序列化为JSON字符串,以及将JSON解析为原生JavaScript值。
let book = {title: '专业js',authors: ["尼克","马特"],edition: 4,year: 2017};let jsonText = JSON.stringify(book);console.log(jsonText);// {"title":"专业js","authors":["尼克","马特"],"edition":4,"year":2017}
JSON.stringify()把一个JavaScript对象序列化为一个JSON字符串。
默认情况下,JSON.stringify()会输出不包含空格或缩进的JSON字符串
在序列化JavaScript对象时,所有函数和原型成员都会有意地在结果中省略。
此外,值为undefined的任何属性也会被跳过。
最终得到的就是所有实例属性均为有效JSON数据类型的表示。
JSON字符串可以直接传给JSON.parse(),然后得到相应的JavaScript值。
let bookCopy = JSON.parse(jsonText);console.log(bookCopy);// {title: "专业js", authors: Array(2), edition: 4, year: 2017}
注:book和bookCopy是两个完全不同的对象,没有任何关系。但是它们拥有相同的属性和值。
如果给JSON.parse()传入的JSON字符串无效,则会导致抛出错误。
23.2.2 序列化选项
JSON.stringify()方法除了要序列化的对象,还可接收两个参数。
这两个参数可用于指定其他序列化JavaScript对象的方式。
第一个参数是过滤器,可以是数组或函数;
第二个参数是用于缩进结果JSON字符串的选项。
单独或组合使用这些参数可以更好地控制JSON序列化。
1.过滤结果
如果第二个参数是一个数组,那么JSON.stringify()返回的结果只会包含该数组中列出的对象属性。
let book = {title: "专业js",authors: ["尼克","马特"],edition: 4,year: 2017};let jsonText = JSON.stringify(book, ["title", "edition"]);console.log(jsonText);// {"title":"专业js","edition":4}
如果第二个参数是一个函数,则行为又有不同。
提供的函数接收两个参数:属性名(key)和属性值(value)。
可根据这个key决定要对相应属性执行什么操作。
这个key始终是字符串,只是在值不属于某个键/值对时会是空字符串。
为了改变对象的序列化,返回的值就是相应key应该包含的结果。
注:返回undefined会导致属性被忽略。
title: "专业js",authors: ["尼克","马特"],edition: 4,year: 2017};let jsonText = JSON.stringify(book, (key, value) => {switch(key) {case "authors":return value.join(",")case "year":return 5000;case "edition":return undefined;default:return value;}});console.log(jsonText);// {"title":"专业js","authors":"尼克,马特","year":5000}
基于键进行了过滤。
如果键是”authors”,则将数组值转换为字符串;
如果键是”year”,则将值设置为5000;
如果键是”edition”,则返回undefined忽略该属性。
最后一定要提供默认返回值,以便返回其他属性传入的值。
第一次调用这个函数实际上会传入空字符串key,值是book对象。
2.字符串缩进
JSON.stringify()方法的第三个参数控制缩进和空格。
在这个参数是数值时,表示每一级缩进的空格数。
3.toJSON()方法
有时对象需要在JSON.stringify()之上自定义JSON序列化。此时,可在要序列化的对象中添加toJSON()方法,序列化时会基于这个方法返回适当的JSON表示。
23.2.3 解析选项
JSON.parse()方法也可接收一个额外的参数,这个函数会针对每个键/值对都调用一次。
为区别于传给JSON.stringify()的起过滤作用的替代函数(replacer),这个函数被称为还原函数(reviver)。、实际上它们的格式完全一样,即:
还原函数也接收两个参数,属性名(key)和属性值(value),另外也需要返回值。
如果还原函数返回undefined,则结果中会删除相应的键。
如果返回了其他任何值,则该值就会成为相应键的值插入到结果中。
还原函数经常被用于把日期字符串转换为Date对象。
