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() 返回单行文本
assert.equal(
JSON.stringify({foo: ['a', 'b']}),
'{"foo":["a","b"]}' );如果为 space 提供一个非负整数,则 .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'不支持的数字:'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(() => {})
undefinedJSON.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 的实例。
assert.deepEqual(
Point.fromJson(JSON.parse('{"x":3,"y":5}')),
new Point(3, 5) );将点转换为 JSON:JSON.stringify() 在内部调用 前面提到的方法 .toJSON()。
assert.equal(
JSON.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,
}
};
assert.equal(
JSON.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) {
log.push({this: this, key, value});
return value; // no change
}
const root = {
a: 1,
b: {
c: 2,
d: 3,
}
};
JSON.stringify(root, valueVisitor);
assert.deepEqual(log, [
{ this: { '': 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,
};
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"
}
}`);要 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"
}
}`;
assert.deepEqual(
JSON.parse(str, reviver),
{
name: 'abc',
regex: /abc/ui,
});Douglas Crockford 在 2012 年 5 月 1 日的 Google+ 帖子 中解释了原因
我从 JSON 中删除了注释,因为我看到人们使用它们来保存解析指令,这种做法会破坏互操作性。我知道缺少注释会让一些人感到难过,但其实不必如此。
假设您正在使用 JSON 来保存配置文件,并且您希望对其进行注释。请随意插入您喜欢的任何注释。然后在将其交给 JSON 解析器之前,将其通过 JSMin [JavaScript 的压缩器] 进行管道传输。