JSON(JavaScript 物件表示法)是一種用於資料儲存的純文字格式。它已成為 Web 服務、組態檔等資料交換格式中相當受歡迎的選擇。ECMAScript 5 有一個 API,可將 JSON 格式的字串轉換為 JavaScript 值(剖析),反之亦然(字串化)。
本節說明 JSON 是什麼以及它是如何建立的。
JSON 將資料儲存為純文字。其語法是 JavaScript 表達式的語法子集。例如:
{
"first"
:
"Jane"
,
"last"
:
"Porter"
,
"married"
:
true
,
"born"
:
1890
,
"friends"
:
[
"Tarzan"
,
"Cheeta"
]
}
JSON 使用 JavaScript 表達式中的下列建構:
null
它遵守下列規則
'mystr'
)是非法的。 Douglas Crockford 在 2001 年發現了 JSON。他為其命名,並在 http://json.org 上建立了規格
我發現了 JSON。我並未宣稱自己發明了 JSON,因為它本來就存在於自然界中。我所做的是找到它、為它命名,並說明它的用途。我並未宣稱自己是第一個發現它的人;我知道在我在發現它的一年前,就已經有其他人發現了它。我發現的最早的例子是,早在 1996 年,就有 Netscape 的某個人使用 JavaScript 陣列文字來進行資料通訊,這至少在我想到這個點子之前五年。
最初,Crockford 希望將 JSON 命名為JavaScript 標記語言,但縮寫 JSML 已被JSpeech 標記語言使用。
JSON 規格已被翻譯成許多人類語言,現在也有許多程式語言的函式庫支援剖析和產生 JSON。
Douglas Crockford 建立了一張 JSON 名片,正面印有標誌(請參閱 圖 22-1),背面則印有完整的語法(請參閱 圖 22-2)。這讓 JSON 有多麼簡單一目了然。
語法可以轉錄如下
{
}
{
成員 }
配對
配對 ,
成員
:
值
[
]
[
元素 ]
值
值 ,
元素
字串
數字
物件
陣列
true
false
null
""
"
字元 "
字元
字元 字元
任何 Unicode 字元(不含 "、\ 或控制字元)
\" \\ \/ \b \f \n \r \t
\u
四個十六進位數字
整數
整數 小數
整數 指數
整數 小數 指數
數字
數字 1-9 數字
-
數字
-
數字 1-9 數字
.
數字
數字
數字 數字
e e+ e-
E E+ E-
全域變數 JSON
用作產生和剖析含有 JSON 資料的字串的函式名稱空間。
JSON.stringify(value, replacer?, space?)
會將 JavaScript 值 value
轉換成 JSON 格式的字串。它有兩個選用引數。
選用參數 replacer
用於在字串化之前變更 value
。它可以是
一個 節點訪客(請參閱 透過節點訪客轉換資料),在字串化之前轉換值樹狀結構。例如
function
replacer
(
key
,
value
)
{
if
(
typeof
value
===
'number'
)
{
value
=
2
*
value
;
}
return
value
;
}
使用 replacer
> JSON.stringify({ a: 5, b: [ 2, 8 ] }, replacer) '{"a":10,"b":[4,16]}'
一個屬性金鑰白名單,用於隱藏所有金鑰不在清單中的屬性(非陣列物件)。例如
> JSON.stringify({foo: 1, bar: {foo: 1, bar: 1}}, ['bar']) '{"bar":{"bar":1}}'
白名單對陣列沒有影響
> JSON.stringify(['a', 'b'], ['0']) '["a","b"]'
選用參數 space
會影響輸出的格式。沒有這個參數,stringify
的結果會是一行文字
> console.log(JSON.stringify({a: 0, b: ['\n']})) {"a":0,"b":["\n"]}
有了它,就會插入換行符,而且透過陣列和物件進行的每一層巢狀結構都會增加縮排。有兩種方式可以指定縮排方式
將數字乘以縮排層級,並使用相同數量的空格縮排該行。小於 0 的數字會解釋為 0;大於 10 的數字會解釋為 10
> console.log(JSON.stringify({a: 0, b: ['\n']}, null, 2)) { "a": 0, "b": [ "\n" ] }
要縮排,請針對每一層縮排重複給定的字串一次。只會使用字串的前 10 個字元
> console.log(JSON.stringify({a: 0, b: ['\n']}, null, '|--')) { |--"a": 0, |--"b": [ |--|--"\n" |--] }
因此,以下 JSON.stringify()
呼叫會將物件印成格式良好的樹狀結構
JSON
.
stringify
(
data
,
null
,
4
)
在物件中,JSON.stringify()
只會考慮可列舉的自身屬性(請參閱 屬性屬性和屬性描述詞)。以下範例示範了不可列舉的自身屬性 obj.foo
被忽略:
> var obj = Object.defineProperty({}, 'foo', { enumerable: false, value: 7 }); > Object.getOwnPropertyNames(obj) [ 'foo' ] > obj.foo 7 > JSON.stringify(obj) '{}'
如何 JSON.stringify()
處理 JSON 不支援的值(例如函式和 undefined
)取決於它在哪裡遇到這些值。一個不受支援的值本身會導致 stringify()
回傳 undefined
,而不是字串
> JSON.stringify(function () {}) undefined
值不受支援的屬性會直接被忽略
> JSON.stringify({ foo: function () {} }) '{}'
陣列中不受支援的值會被字串化為 null
> JSON.stringify([ function () {} ]) '[null]'
如果 JSON.stringify()
遇到一個 具有 toJSON
方法的物件,它會使用該方法取得要字串化的值。例如:
> JSON.stringify({ toJSON: function () { return 'Cool' } }) '"Cool"'
日期已經有一個 toJSON
方法 會產生 ISO 8601 日期字串:
> JSON.stringify(new Date('2011-07-29')) '"2011-07-28T22:00:00.000Z"'
toJSON
方法的完整簽章如下
function
(
key
)
key
參數允許您根據內容不同進行字串化。它永遠都是字串,並指出您的物件在父物件中的位置
我將透過下列物件示範 toJSON()
var
obj
=
{
toJSON
:
function
(
key
)
{
// Use JSON.stringify for nicer-looking output
console
.
log
(
JSON
.
stringify
(
key
));
return
0
;
}
};
如果你使用 JSON.stringify()
,每個 obj
的出現都會被 0
取代。 toJSON()
方法會被通知在屬性鍵 'foo'
和陣列索引 0 中遇到 obj
> JSON.stringify({ foo: obj, bar: [ obj ]}) "foo" "0" '{"foo":0,"bar":[0]}'
內建的 toJSON()
方法如下所示:
Boolean.prototype.toJSON()
Number.prototype.toJSON()
String.prototype.toJSON()
Date.prototype.toJSON()
JSON.parse(text, reviver?)
會分析 text
中的 JSON 資料,並傳回 JavaScript 值。以下是幾個範例:
> JSON.parse("'String'") // illegal quotes SyntaxError: Unexpected token ILLEGAL > JSON.parse('"String"') 'String' > JSON.parse('123') 123 > JSON.parse('[1, 2, 3]') [ 1, 2, 3 ] > JSON.parse('{ "hello": 123, "world": 456 }') { hello: 123, world: 456 }
選用參數 reviver
是 節點訪客(請參閱透過節點訪客轉換資料),可用於轉換已分析的資料。在此範例中,我們將日期字串轉換為日期物件:
function
dateReviver
(
key
,
value
)
{
if
(
typeof
value
===
'string'
)
{
var
x
=
Date
.
parse
(
value
);
if
(
!
isNaN
(
x
))
{
// valid date string?
return
new
Date
(
x
);
}
}
return
value
;
}
以下是互動
> var str = '{ "name": "John", "birth": "2011-07-28T22:00:00.000Z" }'; > JSON.parse(str, dateReviver) { name: 'John', birth: Thu, 28 Jul 2011 22:00:00 GMT }
JSON.stringify()
和 JSON.parse()
都讓你透過傳入函式來轉換 JavaScript 資料:
JSON.stringify()
讓你可以在將 JavaScript 資料轉換為 JSON 之前變更它。
JSON.parse()
會分析 JSON,然後讓你後續處理結果的 JavaScript 資料。
JavaScript 資料是一個樹狀結構,其複合節點為陣列和物件,而其葉節點為基本值(布林值、數字、字串、null
)。讓我們使用名稱 節點訪客 來表示您傳入的轉換函式。這些方法會反覆運算樹狀結構,並對每個節點呼叫訪客。然後,它有選項可以取代或刪除節點。節點訪客的簽章為
function
nodeVisitor
(
key
,
value
)
參數為
this
key
key
永遠是字串。
值
根節點 root
沒有父節點。當 root
被拜訪時,會為它建立一個偽父節點,而參數具有下列值
this
是 { '': root }
。
key
是 ''
。
value
是 root
。
節點訪客有三個選項可以傳回值
value
,維持原狀。然後不會執行任何變更。
undefined
。然後會移除節點。
以下是一個節點訪客範例。它會記錄傳遞給它的值。
function
nodeVisitor
(
key
,
value
)
{
console
.
log
([
// Use JSON.stringify for nicer-looking output
JSON
.
stringify
(
this
),
// parent
JSON
.
stringify
(
key
),
JSON
.
stringify
(
value
)
].
join
(
' # '
));
return
value
;
// don't change node
}
讓我們使用這個函式來檢視 JSON 方法如何反覆運算 JavaScript 資料。
首先是 特殊根節點,以字首反覆運算(父節點在子節點之前)。首先拜訪的節點永遠是偽根節點。每次呼叫後顯示的最後一行是 stringify()
傳回的字串:
> JSON.stringify(['a','b'], nodeVisitor) {"":["a","b"]} # "" # ["a","b"] ["a","b"] # "0" # "a" ["a","b"] # "1" # "b" '["a","b"]' > JSON.stringify({a:1, b:2}, nodeVisitor) {"":{"a":1,"b":2}} # "" # {"a":1,"b":2} {"a":1,"b":2} # "a" # 1 {"a":1,"b":2} # "b" # 2 '{"a":1,"b":2}' > JSON.stringify('abc', nodeVisitor) {"":"abc"} # "" # "abc" '"abc"'
首先是 葉節點,以字尾反覆運算(子節點在父節點之前)。最後拜訪的節點永遠是偽根節點。每次呼叫後顯示的最後一行是 parse()
傳回的 JavaScript 值:
> JSON.parse('["a","b"]', nodeVisitor) ["a","b"] # "0" # "a" ["a","b"] # "1" # "b" {"":["a","b"]} # "" # ["a","b"] [ 'a', 'b' ] > JSON.parse('{"a":1, "b":2}', nodeVisitor) {"a":1,"b":2} # "a" # 1 {"a":1,"b":2} # "b" # 2 {"":{"a":1,"b":2}} # "" # {"a":1,"b":2} { a: 1, b: 2 } > JSON.parse('"hello"', nodeVisitor) {"":"hello"} # "" # "hello" 'hello'