JSON
)JSON
APIJSON.stringify(data, replacer?, space?)
JSON.parse(text, reviver?)
.stringfy()
:指定要字符串化的对象的属性.stringify()
和 .parse()
:值访问器JSON(“JavaScript 对象表示法”)是一种使用文本编码数据的存储格式。它的语法是 JavaScript 表达式的子集。例如,请考虑存储在文件 jane.json
中的以下文本
{"first": "Jane",
"last": "Porter",
"married": true,
"born": 1890,
"friends": [ "Tarzan", "Cheeta" ]
}
JavaScript 具有全局命名空间对象 JSON
,它提供了用于创建和解析 JSON 的方法。
Douglas Crockford 于 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(data, replacer?, space?)
.stringify()
将 JavaScript data
转换为 JSON 字符串。在本节中,我们将忽略参数 replacer
;它在 §45.4 “自定义字符串化和解析” 中进行了解释。
如果只提供第一个参数,则 .stringify()
返回单行文本
.equal(
assertJSON.stringify({foo: ['a', 'b']}),
'{"foo":["a","b"]}' );
如果为 space
提供一个非负整数,则 .stringify()
返回一行或多行,并按每级嵌套 space
个空格缩进
.equal(
assertJSON.stringify({foo: ['a', 'b']}, null, 2),
`{
"foo": [
"a",
"b"
]
}`);
原始值
支持的原始值按预期进行字符串化
> JSON.stringify('abc')'"abc"'
> JSON.stringify(123)'123'
> JSON.stringify(null)'null'
不支持的数字:'null'
> JSON.stringify(NaN)'null'
> JSON.stringify(Infinity)'null'
BigInt:TypeError
> JSON.stringify(123n)TypeError: Do not know how to serialize a BigInt
其他不支持的原始值不会被字符串化;它们会产生结果 undefined
> JSON.stringify(undefined)undefined
> JSON.stringify(Symbol())undefined
对象
如果对象有一个方法 .toJSON()
,则该方法的结果将被字符串化
> JSON.stringify({toJSON() {return true}})'true'
日期有一个方法 .toJSON()
,它返回一个字符串
> JSON.stringify(new Date(2999, 11, 31))'"2999-12-30T23:00:00.000Z"'
包装的原始值将被解包并字符串化
> JSON.stringify(new Boolean(true))'true'
> JSON.stringify(new Number(123))'123'
数组被字符串化为数组字面量。不支持的数组元素将被字符串化为 null
> JSON.stringify([undefined, 123, Symbol()])'[null,123,null]'
所有其他对象(函数除外)都被字符串化为对象字面量。具有不支持的值的属性将被省略
> JSON.stringify({a: Symbol(), b: true})'{"b":true}'
函数不会被字符串化
> JSON.stringify(() => {})undefined
JSON.parse(text, reviver?)
.parse()
将 JSON text
转换为 JavaScript 值。在本节中,我们将忽略参数 reviver
;它在 §45.4 “自定义字符串化和解析” 中进行了解释。
这是使用 .parse()
的示例
> JSON.parse('{"foo":["a","b"]}'){ foo: [ 'a', 'b' ] }
以下类实现了从(A 行)到(B 行)JSON 的转换。
class Point {
static fromJson(jsonObj) { // (A)
return new Point(jsonObj.x, jsonObj.y);
}
constructor(x, y) {
this.x = x;
this.y = y;
}
toJSON() { // (B)
return {x: this.x, y: this.y};
} }
将 JSON 转换为点:我们使用静态方法 Point.fromJson()
来解析 JSON 并创建 Point
的实例。
.deepEqual(
assertPoint.fromJson(JSON.parse('{"x":3,"y":5}')),
new Point(3, 5) );
将点转换为 JSON:JSON.stringify()
在内部调用 前面提到的方法 .toJSON()
。
.equal(
assertJSON.stringify(new Point(3, 5)),
'{"x":3,"y":5}' );
练习:将对象转换为 JSON 和从 JSON 转换
exercises/json/to_from_json_test.mjs
字符串化和解析可以自定义如下
JSON.stringify(data, replacer?, space?)
可选参数 replacer
包含以下任一项
data
中的值被字符串化为对象字面量,则只考虑提到的属性。所有其他属性都将被忽略。JSON.parse(text, reviver?)
可选参数 reviver
包含一个值访问器,它可以在返回之前转换解析的 JSON 数据。
.stringfy()
:指定要字符串化的对象的属性如果 .stringify()
的第二个参数是一个数组,则结果中只包含名称在其中提到的对象属性
const obj = {
a: 1,
b: {
c: 2,
d: 3,
};
}.equal(
assertJSON.stringify(obj, ['b', 'c']),
'{"b":{"c":2}}');
.stringify()
和 .parse()
:值访问器我所说的*值访问器*是一个转换 JavaScript 数据的函数
JSON.stringify()
允许其参数 replacer
中的值访问器在字符串化之前转换 JavaScript 数据。JSON.parse()
允许其参数 reviver
中的值访问器在返回之前转换解析的 JavaScript 数据。在本节中,JavaScript 数据被视为值树。如果数据是原子的,则它是一棵只有根节点的树。树中的所有值都一次一个地提供给值访问器。根据访问器返回的内容,当前值将被省略、更改或保留。
值访问器具有以下类型签名
type ValueVisitor = (key: string, value: any) => any;
参数是
value
:当前值。this
:当前值的父级。根值 r
的父级是 {'': r}
。this
是一个隐式参数,只有在值访问器是普通函数时才可用。key
:当前值在其父级中的键或索引。根值的键是 ''
。值访问器可以返回
value
:表示没有任何变化。x
:导致 value
在输出树中被替换为 x
。undefined
:导致 value
在输出树中被省略。以下代码显示了值访问器查看值的顺序
const log = [];
function valueVisitor(key, value) {
.push({this: this, key, value});
logreturn value; // no change
}
const root = {
a: 1,
b: {
c: 2,
d: 3,
};
}JSON.stringify(root, valueVisitor);
.deepEqual(log, [
assertthis: { '': root }, key: '', value: root },
{ this: root , key: 'a', value: 1 },
{ this: root , key: 'b', value: root.b },
{ this: root.b , key: 'c', value: 2 },
{ this: root.b , key: 'd', value: 3 },
{ ; ])
我们可以看到,JSON.stringify()
的替换器自顶向下访问值(根节点优先,叶节点最后)。朝这个方向进行的理由是我们正在将 JavaScript 值转换为 JSON 值。单个 JavaScript 对象可能会扩展为 JSON 兼容值的树。
相反,JSON.parse()
的还原器自底向上访问值(叶节点优先,根节点最后)。朝这个方向进行的理由是我们正在将 JSON 值组装成 JavaScript 值。因此,我们需要先转换部分,然后才能转换整体。
JSON.stringify()
对正则表达式对象没有特殊支持——它将它们字符串化为普通对象
const obj = {
name: 'abc',
regex: /abc/ui,
;
}.equal(
assertJSON.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
}
}.equal(
assertJSON.stringify(obj, replacer, 2),
`{
"name": "abc",
"regex": {
"__type__": "RegExp",
"source": "abc",
"flags": "iu"
}
}`);
要 JSON.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"
}
}`;
.deepEqual(
assertJSON.parse(str, reviver),
{name: 'abc',
regex: /abc/ui,
; })
Douglas Crockford 在 2012 年 5 月 1 日的 Google+ 帖子 中解释了原因
我从 JSON 中删除了注释,因为我看到人们使用它们来保存解析指令,这种做法会破坏互操作性。我知道缺少注释会让一些人感到难过,但其实不必如此。
假设您正在使用 JSON 来保存配置文件,并且您希望对其进行注释。请随意插入您喜欢的任何注释。然后在将其交给 JSON 解析器之前,将其通过 JSMin [JavaScript 的压缩器] 进行管道传输。