0%

json

把它当成一种数据格式,而不是编程语言。JSON 不属于 JavaScript,
它们只是拥有相同的语法而已。JSON 也不是只能在 JavaScript 中使用,它是一种通用数据格式。很多语言都有解析和序列化 JSON 的内置能力。

语法

  • 简单值:字符串、数值、布尔值和 null 可以在 JSON 中出现,就像在 JavaScript 中一样。特殊值 undefined 不可以。
  • 对象:第一种复杂数据类型,对象表示有序键/值对。每个值可以是简单值,也可以是复杂类型。
  • 数组:第二种复杂数据类型,数组表示可以通过数值索引访问的值的有序列表。数组的值可以是任意类型,包括简单值、对象,甚至其他数组。

简单值

JavaScript 字符串与 JSON 字符串的主要区别是,JSON 字符串必须使用双引号(单引号会导致语法错误)。布尔值和 null 本身也是有效的 JSON 值

对象

与 JavaScript 对象字面量相比,JSON 主要有两处不同。首先,没有变量声明(JSON 中没有变量)。其次,最后没有分号(不需要,因为不是 JavaScript 语句)。同样,用引号将属性名包围起来才是有效的JSON。属性的值可以是简单值或复杂数据类型值,后者可以在对象中再嵌入对象.

1
2
3
4
5
6
7
8
9
{
"name": "Nicholas",
"age": 29,
"school": {
"name": "Merrimack College",
"location": "North Andover, MA"
}
}

数组

数组在 JSON 中使用 JavaScript 的数组字面量形式表示.

JavaScript

1
2
let values = [25, "hi", true];

Json

1
[25,"hi",true]

JavaScript序列化为Json

stringfy():在序列化 JavaScript 对象时,所有函数和原型成员都会有意地在结果中省略。此外,值为 undefined的任何属性也会被跳过。最终得到的就是所有实例属性均为有效 JSON 数据类型的表

1
2
3
4
5
6
7
8
9
10
11
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book);

结果:

{“title”:”Professional JavaScript”,”authors”:[“Nicholas C. Zakas”,”Matt Frisbie”],
“edition”:4,”year”:2017}

还可以接收两个参数。这两个参数可以用于指定其他序列化 JavaScript 对象的方式。第一个参数是过滤器,可以是数组或函数;第二个参数是用于缩进结果 JSON 字符串的选项

1
2
3
4
5
6
7
8
9
10
11
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book, ["title", "edition"]);

  • 如果第二个参数是一个函数,则行为又有不同。提供的函数接收两个参数:属性名(key)和属性值(value)。可以根据这个 key 决定要对相应属性执行什么操作。这个 key 始终是字符串,只是在值
    不属于某个键/值对时会是空字符串
  • 返回的值就是相应 key 应该包含的结果。注意,返回 undefined 会导致属性被忽略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
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;
}
});
/*{"title":"Professional JavaScript","authors":"Nicholas C. Zakas,Matt
Frisbie","year":5000}*/


JSON.stringify()方法的第三个参数控制缩进和空格。在这个参数是数值时,表示每一级缩进的空格数。

1
2
3
4
5
6
7
8
9
10
11
12
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017
};

let jsonText = JSON.stringify(book, null, 4);

toJSON():在要序列化的对象中添加 toJSON()方法,序列化时会基于这个方法返回适当的 JSON 表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017,
toJSON: function() {//箭头函数不能定义toJSON(),因为剪头函数的词法作用域是全局作用域,在这种情况不合适
return this.title;
}
};
let jsonText = JSON.stringify(book);//book对象返回图书的书名(this.title)

toJSON()方法可以与过滤函数一起使用,在把对象传给 JSON.stringify()时会执行如下步骤。

(1) 如果可以获取实际的值,则调用 toJSON()方法获取实际的值,否则使用默认的序列化。
(2) 否则,使用默认序列化时,如果提供了第二个参数,则应用过滤。
(3) 第(2)步返回的每个值都会相应地进行序列化。
(4) 如果提供了第三个参数,则相应地进行缩进。

ES6中堆JSON.stringfy的改造

UTF-8 标准规定,0xD8000xDFFF之间的码点,不能单独使用,必须配对使用。比如,\uD834\uDF06是两个码点,但是必须放在一起配对使用,代表字符𝌆。这是为了表示码点大于0xFFFF的字符的一种变通方法。单独使用\uD834\uDFO6这两个码点是不合法的,或者颠倒顺序也不行,因为\uDF06\uD834并没有对应的字符。

JSON.stringify()的问题在于,它可能返回0xD8000xDFFF之间的单个码点。

1
JSON.stringify('\u{D834}') // "\u{D834}"

为了确保返回的是合法的 UTF-8 字符,ES2019 改变了JSON.stringify()的行为。如果遇到0xD8000xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。

1
JSON.stringify('\u{D834}') // ""\\uD834""JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""

解析选项

JSON.parse():接收一个参数,这个函数称为还原函数,还原函数接收两个参数,属性名key和属性名value,如果还原函数返回undefined,则结果中删除相应键,如果返回了其他任何值,则该值就会成为相应键的值插入到结果中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas",
"Matt Frisbie"
],
edition: 4,
year: 2017,
releaseDate: new Date(2017, 11, 1)
};
let jsonText = JSON.stringify(book);
let bookCopy = JSON.parse(jsonText,
(key, value) => key == "releaseDate" ? new Date(value) : value);
alert(bookCopy.releaseDate.getFullYear());//对象仙贝序列化为JSON字符串,又被重新解析为一个对象bookCopy,还原函数查找"releaseDate"键,找到后根虎日期字符创建新的Date对象,得到的bookCopy.releaseDate属性又变回Date对象,可以调用其getFullYear()方法