JSON
前言 : JavaScript 对象简谱(JSON,JavaScript Object Notation)标准,JSON 是 JavaScript 的严格子集,利用 JavaScript 中的几种模式来表示结构化数据。理解 JSON 最关键的一点是要把它当成一种数据格式,而不是编程语言。JSON 不属于 JavaScript,它们只是拥有相同的语法而已。JSON也不是只能在 JavaScript 中使用,它是一种通用数据格式。很多语言都有解析和序列化 JSON 的内置能力。
JSON 语法
支持三种类型
- 简单值 : 字符串、数值、布尔值和 null 可以在 JSON 中出现,就像在 JavaScript 中一样。特殊值
undefined 不可以 - 对象 : 第一种复杂数据类型,对象表示有序键/值对。每个值可以是简单值,也可以是复杂类型。
- 数组 : 第二种复杂数据类型,数组表示可以通过数值索引访问的值的有序列表。数组的值可以是任意类型,包括简单值、对象,甚至其他数组。
注意 : JSON 没有变量、函数或对象实例的概念。JSON 的所有记号都只为表示结构化数据,虽然它借用了JavaScript 的语法,但是千万不要把它跟JavaScript 语言混淆。
简单值
- 最简单的 JSON 可以是一个数值。
- 举个栗子 :
- 布尔值和
null 本身也是有效的 JSON 值。
注意 : JavaScript 字符串与 JSON 字符串的主要区别是,JSON 字符串必须使用双引号(单引号会导致语法错误)。
对象
-
对象使用与 JavaScript 对象字面量略为不同的方式表示。
-
以下是 JavaScript 中的对象字面量: -
let object = {
"name" : "Nicholas",
"age" :29
};
-
而 JSON 的表示对象语法是 -
{
"name": "Nicholas",
"age": 29
}
-
与 JavaScript 对象字面量相比,JSON 主要有两处不同。首先,没有变量声明(JSON 中没有变量)。其次,最后没有分号(不需要,因为不是 JavaScript 语句)。同样,用引号将属性名包围起来才是有效的 JSON 。 -
JavaScript 和JSON 的属性的值都可以是简单值或复杂数据类型值,而JSON 可以在对象中再嵌入对象。
-
比如 :JSON数据 -
{
"name": "Nicholas",
"age": 29,
"school": {
"name": "Merrimack College",
"location": "North Andover, MA"
}
}
-
上述例子中即使整个 JSON 对象中有两个属性都叫 “name” ,但它们属于两个不同的对象,一个是外面对象的"name"属性,另个是里面 “school” 对象的 “name” 属性,因此是允许的。同一个对象中不允许出现两个相同的属性。 -
与 JavaScript 不同,JSON 中的对象属性名必须始终带双引号。手动编写 JSON 时漏掉这些双引号或使用单引号是常见错误。
数组
-
JSON 的第二种复杂数据类型是数组。 -
数组在 JSON 中使用 JavaScript 的数组字面量形式表示。
-
在 JSON 中可以使用类似语法表示相同的数组:
-
同样,JSON没有变量,也没有分号。数组和对象可以组合使用,以表示更加复杂的数据结构
-
[
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas",
"Matt Frisbie"
],
"edition": 4,
"year": 2017
},
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
"edition": 3,
"year": 2011
},
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
"edition": 2,
"year": 2009
},
{
"title": "Professional Ajax",
"authors": [
"Nicholas C. Zakas",
"Jeremy McPeak",
"Joe Fawcett"
],
"edition": 2,
"year": 2008
},
{
"title": "Professional Ajax",
"authors": [
"Nicholas C. Zakas",
"Jeremy McPeak",
"Joe Fawcett"
],
"edition": 1,
"year": 2007
},
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
"edition": 1,
"year": 2006
}
]
-
对象和数组通常会作为 JSON 数组的顶级结构,以便创建大型复杂数据结构。
解析和序列化
JSON 的迅速流行并不仅仅因为其语法与 JavaScript 类似,很大程度上还因为 JSON 可以直接被解析成可用的 JavaScript 对象。与解析为 DOM 文档的 XML 相比,这个优势非常明显。为此,JavaScript 开发者可以非常方便地使用 JSON 数据。
比如 : JSON数据中books数组
[
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas",
"Matt Frisbie"
],
"edition": 4,
"year": 2017
},
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
"edition": 3,
"year": 2011
},
{
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
"edition": 2,
"year": 2009
}
]
获取第二本书的书名 : books[1].title
JSON 对象
早期的 JSON 解析器基本上就相当于 JavaScript 的 eval() 函数。因为 JSON 是 JavaScript 语法的子集,所以 eval() 可以解析、解释,并将其作为JavaScript 对象和数组返回。
-
JSON 对象在JavaScript 中有两个方法 :
stringify() : 将JavaScript 序列化为 JSON 字符串parse() : 将 JSON 解析为原生 JavaScript 值 -
举个栗子 :
-
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book);
-
在序列化 JavaScript 对象时,所有函数和原型成员都会有意地在结果中省略。此外,值为 undefined 的任何属性也会被跳过。最终得到的就是所有实例属性均为有效 JSON 数据类型的表示。 -
JSON 字符串可以直接传给 JSON.parse() ,然后得到相应的 JavaScript 值。
-
比如,可以使用以下代码创建与 book 对象类似的新对象: -
let bookCopy = JSON.parse(jsonText);
-
注意, book 和 bookCopy 是两个完全不同的对象,没有任何关系。但是它们拥有相同的属性和值。 -
JSON.parse() 传入的JSON字符串必须是有效的,如果传入的 JSON 字符串无效,则会导致抛出错误。
序列化选项
JavaScript 中 JSON.stringify() 方法除了要序列化的对象,还可以接收两个参数。- 这两个参数可以用于指定其他序列化
JavaScript 对象的方式。
- 第一个参数是过滤器,可以是数组或函数;第二个参数是用于缩进结果 JSON 字符串的选项。
- 单独或组合使用这些参数可以更好地控制 JSON 序列化。
- 过滤结果
- 字符串缩进
-
JSON.stringify() 方法的第三个参数控制缩进和空格。在这个参数是数值时,表示每一级缩进的空格数。 -
说明 : 如果第二个参数不需要填的话,就必须要用关键字null 来代替
-
比如 : let jsonText1 = JSON.stringify(book, null, 2); -
举个大栗子 : -
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript序列化</title>
</head>
<body>
<script>
let book = {
title: "富婆通讯录",
authors: [
"CS@zeny",
"各大富婆"
],
edition: 1,
year: 2021,
price: "100yuan"
};
let jsonText1 = JSON.stringify(book, null, 2);
let jsonText2 = JSON.stringify(book, null, 4);
console.log(jsonText1);
console.log(jsonText2);
</script>
</body>
</html>
-
输出效果 : -
-
注意,除了缩进, JSON.stringify() 方法还为方便阅读插入了换行符。这个行为对于所有有效的缩进参数都会发生。(只缩进不换行也没什么用。)最大缩进值为 10,大于 10 的值会自动设置为 10。 -
如果缩进参数是一个字符串而非数值,那么JSON字符串中就会使用这个字符串而不是空格来缩进。使用字符串,也可以将缩进字符设置为 Tab 或任意字符。
-
如两个连字符-- , 一个Tab -
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript序列化</title>
</head>
<body>
<script>
let book = {
title: "富婆通讯录",
authors: [
"CS@zeny",
"各大富婆"
],
edition: 1,
year: 2021,
price: "100yuan"
};
let jsonText1 = JSON.stringify(book, null, "--");
let jsonText2 = JSON.stringify(book, null, " ");
let jsonText3 = JSON.stringify(book, null, 4);
console.log(jsonText1);
console.log(jsonText2);
console.log(jsonText3);
</script>
</body>
</html>
-
效果 : -
注意 : 使用字符串时同样有 10 个字符的长度限制。如果字符串长度超过 10,则会在第 10 个字符处截断。
toJSON() 方法
-
当对象需要在 JSON.stringify() 之上自定义 JSON 序列化。此时,可以在要序列化的对象中添加 toJSON() 方法,序列化时会基于这个方法返回适当的 JSON 表示。 -
事实上,原生 Date 对象就有一个 toJSON() 方法,能够自动将 JavaScript 的 Date 对象转换为 ISO 8601 日期字符串(本质上与在Date 对象上调用 toJSON() 方法一样)。
-
举个栗子 : -
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript序列化</title>
</head>
<body>
<script>
let book = {
title: "富婆通讯录",
authors: [
"CS@zeny",
"各大富婆"
],
edition: 1,
year: 2021,
price: "100yuan",
toJSON: function() {
return this.title;
}
};
let jsonText = JSON.stringify(book);
console.log(jsonText);
</script>
</body>
</html>
-
输出结果 : "富婆通讯录" -
toJSON() 方法可以返回任意序列化值,都可以起到相应的作用。如果对象被嵌入在另一个对象中,返回 undefined 会导致值变成 null ;或者如果是顶级对象,则本身就是 undefined 。
注意,箭头函数不能用来定义 toJSON() 方法。主要原因是箭头函数的词法作用域是全局作用域,在这种情况下不合适。
toJSON() 方法可以与过滤函数一起使用,因此理解不同序列化流程的顺序非常重要。在把对象传给 JSON.stringify() 时会执行如下步骤。-
- 如果可以获取实际的值,则调用
toJSON() 方法获取实际的值,否则使用默认的序列化。 - 如果提供了第二个参数,则应用过滤。传入过滤函数的值就是第 1 步返回的值。
- 第 2 步返回的每个值都会相应地进行序列化。
- 如果提供了第三个参数,则相应地进行缩进。理解这个顺序有助于决定是创建
toJSON() 方法,还是使用过滤函数,或是两者都用。
解析选项
JSON.parse() 方法也可以接收一个额外的参数,这个函数会针对每个键/值对都调用一次。- 为区别于传给
JSON.stringify() 的起过滤作用的替代函数(replacer),这个函数被称为还原函数(reviver)。
- 实际上它们的格式完全一样,即还原函数也接收两个参数,属性名( key )和属性值( value ),另外也需要返回值。
- 如果还原函数返回
undefined ,则结果中就会删除相应的键。如果返回了其他任何值,则该值就会成为相应键的值插入到结果中。还原函数经常被用于把日期字符串转换为 Date 对象。
举个栗子 :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JSON解析</title>
</head>
<body>
<script>
let book = {
title: "富婆通讯录",
authors: [
"CS@zeny",
"各大富婆"
],
edition: 1,
year: 2021,
price: "100yuan",
releaseDate: new Date(2021, 01, 01)
};
let jsonText = JSON.stringify(book);
let bookCopy = JSON.parse(jsonText,
(key, value) => key == "releaseDate" ? new Date(value) : value);
alert("返回的年份:" + bookCopy.releaseDate.getFullYear());
</script>
</body>
</html>
效果 :
附注 : getFullYear() 是Date类下的方法;作用 : 返回 4 位数年(即 2021而不是 21)
JSON 的优点
JSON 是一种轻量级数据格式,可以方便地表示复杂数据结构。这个格式使用 JavaScript 语法的一个子集表示对象、数组、字符串、数值、布尔值和 null 。虽然 XML 也能胜任同样的角色,但 JSON 更简洁,JavaScript 支持也更好。更重要的是,所有浏览器都已经原生支持全局 JSON 对象。
ENDING
参考书籍 : Javascript高级程序设计:第4版/ (美)马特·弗里斯比(Matt Frisbie)著;李松峰译. 北京:人民邮电出版社,2020.9 第23章 JSON P704.
|