JSON
)JSON
API
JSON.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 的壓縮器] 進行處理。