JSON
JSON
命名空间对象包含用于从 JavaScript 对象表示法 (JSON) 解析值以及将值转换为 JSON 的静态方法。
描述
JavaScript 和 JSON 的区别
JSON 是一种用于序列化对象、数组、数字、字符串、布尔值和 null
的语法。它基于 JavaScript 语法,但与 JavaScript 不同:大多数 JavaScript 不是 JSON。例如
任何 JSON 文本都是有效的 JavaScript 表达式,但只有在 JSON 超集 修订之后。在修订之前,U+2028 行分隔符和 U+2029 段落分隔符在 JSON 的字符串文字和属性键中允许使用;但在 JavaScript 字符串文字中使用相同的内容会导致 SyntaxError
。
其他区别包括只允许双引号字符串,以及不支持 undefined
或注释。对于那些希望使用更人性化的基于 JSON 的配置格式的人,可以使用由 Babel 编译器使用的 JSON5,以及更常用的 YAML。
相同的文本在 JavaScript 对象文字和 JSON 中也可能表示不同的值。有关更多信息,请参见 对象文字语法与 JSON。
完整的 JSON 语法
有效的 JSON 语法由以下语法正式定义,该语法用 ABNF 表示,并从 IETF JSON 标准 (RFC) 中复制而来
JSON-text = object / array begin-array = ws %x5B ws ; [ left square bracket begin-object = ws %x7B ws ; { left curly bracket end-array = ws %x5D ws ; ] right square bracket end-object = ws %x7D ws ; } right curly bracket name-separator = ws %x3A ws ; : colon value-separator = ws %x2C ws ; , comma ws = *( %x20 / ; Space %x09 / ; Horizontal tab %x0A / ; Line feed or New line %x0D ; Carriage return ) value = false / null / true / object / array / number / string false = %x66.61.6c.73.65 ; false null = %x6e.75.6c.6c ; null true = %x74.72.75.65 ; true object = begin-object [ member *( value-separator member ) ] end-object member = string name-separator value array = begin-array [ value *( value-separator value ) ] end-array number = [ minus ] int [ frac ] [ exp ] decimal-point = %x2E ; . digit1-9 = %x31-39 ; 1-9 e = %x65 / %x45 ; e E exp = e [ minus / plus ] 1*DIGIT frac = decimal-point 1*DIGIT int = zero / ( digit1-9 *DIGIT ) minus = %x2D ; - plus = %x2B ; + zero = %x30 ; 0 string = quotation-mark *char quotation-mark char = unescaped / escape ( %x22 / ; " quotation mark U+0022 %x5C / ; \ reverse solidus U+005C %x2F / ; / solidus U+002F %x62 / ; b backspace U+0008 %x66 / ; f form feed U+000C %x6E / ; n line feed U+000A %x72 / ; r carriage return U+000D %x74 / ; t tab U+0009 %x75 4HEXDIG ) ; uXXXX U+XXXX escape = %x5C ; \ quotation-mark = %x22 ; " unescaped = %x20-21 / %x23-5B / %x5D-10FFFF HEXDIG = DIGIT / %x41-46 / %x61-66 ; 0-9, A-F, or a-f ; HEXDIG equivalent to HEXDIG rule in [RFC5234] DIGIT = %x30-39 ; 0-9 ; DIGIT equivalent to DIGIT rule in [RFC5234]
不重要的 空白 可以出现在任何地方,除了在 JSONNumber
(数字必须不包含空白)或 JSONString
(在那里它被解释为字符串中的相应字符,或者会导致错误)中。制表符 (U+0009)、回车符 (U+000D)、换行符 (U+000A) 和空格 (U+0020) 字符是唯一有效的空白字符。
静态属性
JSON[Symbol.toStringTag]
-
[Symbol.toStringTag]
属性的初始值为字符串"JSON"
。此属性用于Object.prototype.toString()
。
静态方法
JSON.isRawJSON()
实验性-
测试一个值是否是由
JSON.rawJSON()
返回的对象。 JSON.parse()
-
将一段字符串文本解析为 JSON,可以选择转换生成的 value 和它的属性,然后返回这个 value。
JSON.rawJSON()
实验性-
创建一个包含一段 JSON 文本的“原始 JSON”对象。当序列化为 JSON 时,原始 JSON 对象将被视为已经是 JSON 片段。此文本必须是有效的 JSON。
JSON.stringify()
-
返回与指定值相对应的 JSON 字符串,可以选择只包含某些属性或以用户定义的方式替换属性值。
示例
JSON 示例
{
"browsers": {
"firefox": {
"name": "Firefox",
"pref_url": "about:config",
"releases": {
"1": {
"release_date": "2004-11-09",
"status": "retired",
"engine": "Gecko",
"engine_version": "1.7"
}
}
}
}
}
您可以使用 JSON.parse()
方法将上面的 JSON 字符串转换为 JavaScript 对象
const jsonText = `{
"browsers": {
"firefox": {
"name": "Firefox",
"pref_url": "about:config",
"releases": {
"1": {
"release_date": "2004-11-09",
"status": "retired",
"engine": "Gecko",
"engine_version": "1.7"
}
}
}
}
}`;
console.log(JSON.parse(jsonText));
无损数字序列化
JSON 可以包含任意精度的数字文字。但是,无法在 JavaScript 中精确地表示所有 JSON 数字,因为 JavaScript 使用具有固定精度的浮点表示法。例如,在 JavaScript 中,12345678901234567890 === 12345678901234567000
,因为它们具有相同的浮点表示。这意味着没有与 12345678901234567890
JSON 数字精确对应的 JavaScript 数字。
假设您对某个数字有精确的表示(通过 BigInt
或自定义库)。
const data = {
// Using a BigInt here to store the exact value,
// but it can also be a custom high-precision number library,
// if the number might not be an integer.
gross_gdp: 12345678901234567890n,
};
您希望将其序列化然后解析回相同的精确数字。有几个困难
- 在序列化方面,为了获得 JSON 中的数字,您必须将数字传递给
JSON.stringify
,无论是通过replacer
函数还是通过toJSON
方法。但是,在这两种情况下,您已经在数字转换过程中丢失了精度。如果您将字符串传递给JSON.stringify
,它将被序列化为字符串,而不是数字。 - 在解析方面,并非所有数字都能精确地表示。例如,
JSON.parse("12345678901234567890")
返回12345678901234568000
,因为该数字被四舍五入到最接近的可表示数字。即使您使用reviver
函数,数字也会在reviver
函数被调用之前被四舍五入。
一般来说,有两种方法可以确保数字被无损地转换为 JSON 并解析回来:一种涉及 JSON 数字,另一种涉及 JSON 字符串。JSON 是一种通信格式,所以如果您使用 JSON,您可能正在与另一个系统通信(HTTP 请求、存储在数据库中,等等)。选择最佳解决方案取决于接收系统。
使用 JSON 字符串
如果接收系统不具备与 JavaScript 相同的 JSON 处理能力,也不支持高精度数字,您可能希望将数字序列化为字符串,然后在接收方将其视为字符串处理。这也是旧版 JavaScript 中的唯一选项。
要指定如何将自定义数据类型(包括 BigInt
)序列化为 JSON,请为您的数据类型添加 toJSON
方法,或者使用 JSON.stringify()
的 replacer
函数。
// Using toJSON() method
BigInt.prototype.toJSON = function () {
return this.toString();
};
const str1 = JSON.stringify(data);
// Using JSON.stringify() with replacer
const str2 = JSON.stringify(data, (key, value) => {
if (key === "gross_gdp") {
return value.toString();
}
return value;
});
在这两种情况下,JSON 文本将类似于 {"gross_gdp":"12345678901234567890"}
,其中 value 是字符串,而不是数字。然后,在接收方,您可以解析 JSON 并处理字符串。
使用 JSON 数字
如果此消息的接收者原生支持高精度数字(如 Python 整数),将数字作为 JSON 数字传递显然更好,因为它们可以直接解析为高精度类型,而不是从 JSON 中解析字符串,然后从字符串中解析数字。在 JavaScript 中,您可以使用 JSON.rawJSON()
将任意数据类型序列化为 JSON 数字,而无需先生成数字值(导致精度丢失),从而精确地指定 JSON 源文本应该是什么。
// Using toJSON() method
BigInt.prototype.toJSON = function () {
return JSON.rawJSON(this.toString());
};
const str1 = JSON.stringify(data);
// Using JSON.stringify() with replacer
const str2 = JSON.stringify(data, (key, value) => {
if (key === "gross_gdp") {
return JSON.rawJSON(value.toString());
}
return value;
});
传递给 JSON.rawJSON
的文本被视为已经是 JSON 片段,因此不会再次被序列化为字符串。因此,JSON 文本将类似于 {"gross_gdp":12345678901234567890}
,其中 value 是数字。然后,接收者可以在没有任何额外处理的情况下解析此 JSON,前提是接收者系统没有与 JavaScript 相同的精度限制。
在解析包含高精度数字的 JSON 时,请格外小心,因为当 JSON.parse()
调用 reviver
函数时,您接收到的 value 已经被解析(并已丢失精度)。您可以使用 JSON.parse()
reviver
函数的 context.source
参数重新解析数字。
const parsedData = JSON.parse(str, (key, value, context) => {
if (key === "gross_gdp") {
// Or use the constructor of your custom high-precision number library
return BigInt(context.source);
}
return value;
});
// { gross_gdp: 12345678901234567890n }
规范
规范 |
---|
ECMAScript 语言规范 # sec-json-object |
浏览器兼容性
BCD 表格只在浏览器中加载