JSON

JSON 命名空间对象包含用于从 JavaScript 对象表示法 (JSON) 解析值以及将值转换为 JSON 的静态方法。

描述

与大多数全局对象不同,JSON 不是构造函数。您不能使用 new 运算符 或将 JSON 对象作为函数调用。JSON 的所有属性和方法都是静态的(就像 Math 对象一样)。

JavaScript 和 JSON 的区别

JSON 是一种用于序列化对象、数组、数字、字符串、布尔值和 null 的语法。它基于 JavaScript 语法,但与 JavaScript 不同:大多数 JavaScript 不是 JSON。例如

对象和数组

属性名称必须是双引号字符串;尾部逗号 是禁止的。

数字

禁止前导零。小数点后必须至少有一位数字。NaNInfinity 不支持。

任何 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 示例

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 对象

js
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 或自定义库)。

js
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 函数。

js
// 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 源文本应该是什么。

js
// 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 参数重新解析数字。

js
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 表格只在浏览器中加载

另请参见