JSON
)JSON(“JavaScript Object Notation”)是一种使用文本对数据进行编码的存储格式。它的语法是 JavaScript 表达式的一个子集。例如,考虑以下数据,以文本形式存储在文件jane.json
中:
{
"first": "Jane",
"last": "Porter",
"married": true,
"born": 1890,
"friends": [ "Tarzan", "Cheeta" ]
}
JavaScript 具有全局命名空间对象JSON
提供了用于创建和解析 JSON 的方法。
道格拉斯克罗克福德于 2001 年在 json.org
上发表了 JSON 规范。他解释说:
我发现了 JSON。我并没有声称发明了 JSON,因为它已经存在于自然界中。我做的是我发现它,我命名它,我描述了它是如何有用的。我并不是第一个发现它的人;我知道还有其他人在我做之前至少一年才发现它。我发现的最早的事情是,早在 1996 年,Netscape 就有人使用 JavaScript 数组字面值进行数据通信,至少在我偶然发现这个想法之前的五年。
后来,JSON 被标准化为 ECMA-404 :
引用 ECMA-404 标准:
因为它非常简单,所以不期望 JSON 语法会发生变化。这使得 JSON 作为一种基本符号,具有极大的稳定性。
因此,JSON 永远不会得到诸如可选的尾随逗号,注释或不带引号的密钥之类的改进 - 无论它们是否被认为是合乎需要的。但是,这仍然留下了创建编译为普通 JSON 的 JSON 超集的空间。
JSON 由以下 JavaScript 部分组成:
null
(但不是undefined
)NaN
,+Infinity
,-Infinity
)因此,您不能(直接)在 JSON 中表示循环结构。
JSON
API全局命名空间对象JSON
包含用于处理 JSON 数据的方法。
JSON.stringify(value, replacer?, space?)
.stringify()
将 JavaScript value
转换为 JSON 字符串。
如果只提供第一个参数,.stringify()
将返回单行文本:
assert.equal(
JSON.stringify({foo: ['a', 'b']}),
`{"foo":["a","b"]}`);
如果你为space
提供一个非负整数(我们在这里忽略replacer
,后面的 解释了),那么.stringify()
会返回一个或多个行和每个space
空格的缩进嵌套水平:
assert.equal(
JSON.stringify({foo: ['a', 'b']}, null, 2),
`{
"foo": [
"a",
"b"
]
}`);
支持的原始值按预期进行字符串化:
> JSON.stringify('abc')
'"abc"'
> JSON.stringify(123)
'123'
> JSON.stringify(null)
'null'
非有限数字(包括NaN
)被字符串化为'null'
:
> JSON.stringify(NaN)
'null'
> JSON.stringify(Infinity)
'null'
不支持的原始值被字符串化为undefined
:
> JSON.stringify(undefined)
undefined
> JSON.stringify(Symbol())
undefined
函数字符串化为undefined
:
> JSON.stringify(() => {})
undefined
在数组中,将字符串化为undefined
的元素被字符串化为'null'
:
> JSON.stringify([undefined, 123, () => {}])
'[null,123,null]'
在一个对象(既不是数组也不是函数)中,将跳过其值将被字符串化为undefined
的属性:
> JSON.stringify({a: Symbol(), b: true})
'{"b":true}'
如果一个对象(可能是一个数组或一个函数)有一个方法.toJSON()
,那么该方法的结果将被字符串化,而不是该对象。例如,日期有一个返回字符串的方法.toJSON()
。
> JSON.stringify({toJSON() {return true}})
'true'
> JSON.stringify(new Date(2999, 11, 31))
'"2999-12-30T23:00:00.000Z"'
有关字符串化的更多详细信息,请参阅 ECMAScript 规范。
JSON.parse(text, reviver?)
.parse()
将 JSON text
转换为 JavaScript 值:
> JSON.parse('{"foo":["a","b"]}')
{ foo: [ 'a', 'b' ] }
以下类演示了一种实现 JSON 转换的技术:
class Point {
static fromJson(jsonObj) {
return new Point(jsonObj.x, jsonObj.y);
}
constructor(x, y) {
this.coord = [x, y];
}
toJSON() {
const [x, y] = this.coord;
return {x, y};
}
}
assert.equal(
JSON.stringify(new Point(3, 5)),
'{"x":3,"y":5}');
assert.deepEqual(
Point.fromJson(JSON.parse('{"x":3,"y":5}')),
new Point(3, 5));
前面提到的 方法.toJSON()
用于字符串化Point
的实例。
exercises/json/to_from_json_test.js
字符串化或解析的内容可以配置如下:
.stringify()
具有可选参数replacer
,其中包含:
.parse()
具有可选参数reviver
- 一个值访问者,可以在返回之前转换已解析的 JSON 数据。.stringfy()
:指定对象应具有的唯一属性如果.stringify()
的第二个参数是一个数组,那么结果中只包含在数组中提到其名称的对象属性:
const obj = {
a: 1,
b: {
c: 2,
d: 3,
}
};
assert.equal(
JSON.stringify(obj, ['b', 'c']),
'{"b":{"c":2}}');
.stringify()
和.parse()
:重视访客我称之为 _ 值访问者 _ 是一个转换 JavaScript 值(复合或原子)的函数:
JSON.stringify()
在其接收到的 JavaScript 值之前调用其参数replacer
中的值 visitor。JSON.parse()
在其参数reviver
中调用值 visitor。JavaScript 值的转换如下:值为原子值或复合值,并包含更多值(嵌套在数组和对象中)。原子值或嵌套值一次一个地馈送到值 vistor。根据访问者返回的内容,将删除,更改或保留当前值。
值访问者具有以下类型签名:
type ValueVisitor = (this: object, key: string, value: any) => any;
参数是:
value
:当前值。this
:当前值的父级。根值r
的父级是{'': r}
。key
:其父级内当前值的键或索引。空字符串用于根值。值访问者可以返回:
value
:表示不会有任何变化。x
:导致value
被x
取代。undefined
:导致value
被移除。以下代码演示了值访问者查看值的顺序。
const log = [];
function valueVisitor(key, value) {
log.push({key, value, this: this});
return value; // no change
}
const root = {
a: 1,
b: {
c: 2,
d: 3,
}
};
JSON.stringify(root, valueVisitor);
assert.deepEqual(log, [
{ key: '', value: root, this: { '': root } },
{ key: 'a', value: 1, this: root },
{ key: 'b', value: root.b, this: root },
{ key: 'c', value: 2, this: root.b },
{ key: 'd', value: 3, this: root.b },
]);
如您所见,.stringify()
从上到下访问值(根首先,最后留下)。相反,.parse()
访问自下而上的值(先离开,最后一根)。
.stringify()
对正则表达式对象没有特殊支持 - 它将它们字符串化为就像它们是普通对象一样:
const obj = {
name: 'abc',
regex: /abc/ui,
};
assert.equal(
JSON.stringify(obj),
'{"name":"abc","regex":{}}');
我们可以通过替换器解决这个问题:
function replacer(key, value) {
if (value instanceof RegExp) {
return {
__type__: 'RegExp',
source: value.source,
flags: value.flags,
};
} else {
return value; // no change
}
}
assert.equal(
JSON.stringify(obj, replacer, 2),
`{
"name": "abc",
"regex": {
"__type__": "RegExp",
"source": "abc",
"flags": "iu"
}
}`);
要.parse()
上一节的结果,我们需要一个复活者:
function reviver(key, value) {
// Very simple check
if (value && value.__type__ === 'RegExp') {
return new RegExp(value.source, value.flags);
} else {
return value;
}
}
const str = `{
"name": "abc",
"regex": {
"__type__": "RegExp",
"source": "abc",
"flags": "iu"
}
}`;
assert.deepEqual(
JSON.parse(str, reviver),
{
name: 'abc',
regex: /abc/ui,
});
道格拉斯·克罗克福德在中解释了为什么从 2012 年 5 月 1 日开始发布一条 Google+信息:
我从 JSON 中删除了注释,因为我看到有人使用它们来保存解析指令,这种做法会破坏互操作性。我知道缺乏评论会让一些人感到悲伤,但事实并非如此。
假设您使用 JSON 来保留您想要注释的配置文件。继续,插入您喜欢的所有评论。然后将它传递给 JSMin [JavaScript 的一个 minifier],然后再将它传递给你的 JSON 解析器。
_ 值访客签名 _:
type ValueVisitor = (this: object, key: string, value: any) => any;
JSON
:
.stringify(value: any, replacer?: ValueVisitor, space?: string | number): string
[ES5]
将value
转换为 JSON 字符串。本章前面解释了参数replacer
。参数space
的工作原理如下:
如果省略space
,.stringify()
将返回单行文本。
assert.equal(
JSON.stringify({foo: 1, bar: [2, 3]}),
'{"foo":1,"bar":[2,3]}');
如果space
是一个数字,.stringify()
返回一行或多行,并按每个嵌套级别的space
空格缩进。
assert.equal(
JSON.stringify({foo: 1, bar: [2, 3]}, null, 2),
`{
"foo": 1,
"bar": [
2,
3
]
}`);
如果space
是一个字符串,则用于缩进。
assert.equal(
JSON.stringify({foo: 1, bar: [2, 3]}, null, '>>'),
`{
>>"foo": 1,
>>"bar": [
>>>>2,
>>>>3
>>]
}`);
.stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string
[ES5]
如果replacer
是一个数组,则结果只包括其名称在数组中提及的对象属性。
> JSON.stringify({foo: 1, bar: 2}, ['foo'])
'{"foo":1}'
.parse(text: string, reviver?: ValueVisitor): any
[ES5]
解析text
中的 JSON 并返回 JavaScript 值。本章前面解释了参数reviver
。
assert.deepEqual(
JSON.parse('{"a":true,"b":[1,2]}'),
{ a: true, b: [1,2] }
);