曾經(jīng)有一段時間,XML是互聯(lián)網(wǎng)上傳輸結(jié)構(gòu)化數(shù)據(jù)的事實(shí)標(biāo)準(zhǔn)。
JSON是JavaScript的一個嚴(yán)格的子集苔严,利用了JavaScript中的一些模式來表示結(jié)構(gòu)化數(shù)據(jù)甩恼。
語法
JSON語法可以表示一下三種類型的值:
- 簡單值:使用與JavaScript相同的語法蟀瞧,可以在JSON中表示字符串、數(shù)值媳拴、布爾值和null黄橘。但JSON不支持JavaScript中的特殊值undefined。
- 對象:對象作為一種復(fù)雜數(shù)據(jù)類型屈溉,表示的是一組有序的鍵值對塞关。而每個鍵值對中的值可以是簡單值,也可以是復(fù)雜數(shù)據(jù)類型的值子巾。
- 數(shù)組:數(shù)組也是一種復(fù)雜數(shù)據(jù)類型帆赢⌒⊙梗可以通過數(shù)值索引來訪問其中的值。數(shù)值的值也可以是任意類型椰于。
簡單值
JavaScript字符串與JSON字符串的最大區(qū)別在于怠益,JSON字符串必須使用雙引號(單引號會導(dǎo)致語法錯誤)。
布爾值和null也是有效的JSON形式瘾婿。
對象
與JavaScript的對象字面量相比蜻牢,JSON對象有兩個不同的地方。首先偏陪,沒有聲明變量抢呆。其次,沒有末尾的分好笛谦。對象的屬性必須加雙引號抱虐。
同一個對象中絕對不應(yīng)該出現(xiàn)兩個同名屬性搓侄。
與JavaScript不同逞刷,JSON中對象的屬性名任何時候都必須加雙引號。
數(shù)組
同樣颂砸,JSON數(shù)組也沒有變量和分號灶轰。
解析與序列化
JSON數(shù)據(jù)結(jié)構(gòu)可以解析為JavaScript對象谣沸。與XML數(shù)據(jù)結(jié)構(gòu)要解析成DOM文檔而且從中提取數(shù)據(jù)極為麻煩,JSON可以解析為JavaScript對象的優(yōu)勢極為明顯框往。
JSON對象
早起的JSON解析器基本上就是使用JavaScript的eval()
函數(shù)鳄抒。
ECMAScript 5對解析JSON的行為進(jìn)行規(guī)范,定義了全局對象JSON椰弊。支持這個對象的瀏覽器有IE8+许溅、Firefox3.5+、Safari 4+秉版、Chrome和Opera 10.5+贤重。對于較早版本的瀏覽器,可以使用一個shim:https://github.com/douglascrockford/JSON-js清焕。在舊版本的瀏覽器中并蝗,使用eval()
對JSON數(shù)據(jù)結(jié)構(gòu)求值存在風(fēng)險,因為可能會執(zhí)行一些惡意代碼秸妥。
JSON對象有兩個方法:stringify()
和parse()
滚停。
在最簡單的情況下,這兩個方法分別用于把JavaScript對象序列化為JSON字符串和把JSON字符串解析為原生JavaScript值粥惧。
默認(rèn)情況下键畴,JSON.stringify()
輸出的JSON字符串不包括任何空格字符或縮進(jìn)。
在序列化JavaScript對象時,所有函數(shù)及原型成員都會被有意忽略起惕,不體現(xiàn)在結(jié)果中涡贱。此外,值為undefined的任何屬性都會被跳過惹想。
如果傳遞給JSON.parse()
的字符串不是有效的JSON问词,該方法會拋出錯誤。
序列化選項
JSON.stringify()
還可以接受另外兩個參數(shù)嘀粱。第一個參數(shù)是個過濾器激挪,可以是一個數(shù)組,也可以是一個函數(shù)草穆;第二個參數(shù)是一個選項灌灾,表示是否在JSON字符串中保留縮進(jìn)。
過濾結(jié)果
如果過濾器參數(shù)是一個數(shù)組悲柱,那么JSON.stringify()
的結(jié)果中將只包含數(shù)組中列出的屬性。
如果第二個參數(shù)是一個函數(shù)些己,傳入的函數(shù)接受兩個參數(shù)豌鸡,屬性(鍵)名和屬性值。根據(jù)屬性(鍵)名可以知道應(yīng)該如何處理要序列化的對象中的屬性段标。屬性名只能是字符串涯冠,而在值并非鍵值對兒結(jié)構(gòu)的值時,鍵名可以是空字符串逼庞。
為了改變序列化對象的結(jié)果蛇更,函數(shù)返回的值就是相應(yīng)鍵的值。不過要注意赛糟,如果函數(shù)返回了undefined派任,那么相應(yīng)的屬性會被忽略。
var book = {
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(bool, function (key, value) {
switch (key) {
case "authors":
return value.join(",");
case "year":
return 5000;
case "edition":
return undefined;
default:
return value;
}
});
Firefox3.5和3.6對JSON.stringify()
的實(shí)現(xiàn)有一個bug璧南,在將函數(shù)作為該方法的第二個參數(shù)時這個bug 會出現(xiàn)掌逛,即這個函數(shù)只能作為過濾器:返回undefined意味著要跳過某個屬性,而返回其他任何值都會在結(jié)果中包含相應(yīng)的屬性司倚。Firefox4修復(fù)了這個bug豆混。
字符串縮進(jìn)
JSON.stringify()
方法的第三個參數(shù)用于控制結(jié)果中的縮進(jìn)和空白符。如果這個參數(shù)是一個數(shù)值动知,那它表示的是每個級別縮進(jìn)的空格數(shù)皿伺。
只要傳入有效的控制縮進(jìn)的參數(shù)值,結(jié)果字符串就會包含換行符盒粮。最大縮進(jìn)空格數(shù)為10鸵鸥,所有大于10的值都會自動轉(zhuǎn)換為10。
如果縮進(jìn)參數(shù)是一個字符串而非數(shù)值拆讯,則這個字符串將在JSON字符串中被用作縮進(jìn)字符(不再使用空格)脂男。在使用字符串的情況下养叛,可以將縮進(jìn)設(shè)置為制表符,或者兩個短劃線之類的任意字符宰翅∑縮進(jìn)字符串最長不能超過10個字符長。如果字符串長度超過了10個汁讼,結(jié)果中將只出現(xiàn)前10個字符淆攻。
toJSON()方法
有時候,JSON.stringify()
還是不能滿足對某些對象進(jìn)行自定義序列化的需求嘿架。在這些情況下瓶珊,可以通過對象上調(diào)用toJSON()
方法,返回其自身的JSON數(shù)據(jù)格式耸彪。
var book = {
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"
],
edition: 3,
year: 2011,
toJSON: function () {
return this.title;
}
};
var jsonText = JSON.stringify(book);
這個對象也將被序列化為一個簡單的字符串而非對象伞芹。可以讓toJSON()
方法返回任何序列化的值蝉娜,它都能正常工作唱较。也可以讓這個方法返回undefined,此時如果包含它的對象嵌入在另一個對象中召川,會導(dǎo)致該對象的值變成null南缓,而如果包含它的對象是頂級對象,結(jié)果就是undefined荧呐。
toJSON()
可以作為函數(shù)過濾器的補(bǔ)充汉形,因此理解序列化的內(nèi)部順序十分重要。假設(shè)把一個對象傳入JSON.stringify()
倍阐,序列化該對象的順序如下概疆。
- 如果存在
toJSON()
方法而且能通過它取得有效的值,則調(diào)用方法收捣。否則届案,按默認(rèn)順序執(zhí)行序列化。 - 如果提供了第二個參數(shù)罢艾,應(yīng)用這個函數(shù)過濾器楣颠。傳入函數(shù)過濾器的值是第一步返回的值。
- 對第二步返回的每個值進(jìn)行相應(yīng)的序列化咐蚯。
- 如果提供了第二個參數(shù)童漩,執(zhí)行相應(yīng)的格式化。
解析選項
JSON.parse()
方法也可以接受另一個參數(shù)春锋,該參數(shù)是一個函數(shù)矫膨,將在每個鍵值對上調(diào)用。這個函數(shù)接受兩個參數(shù),一個鍵和一個值侧馅,并返回一個值危尿。
如果函數(shù)返回undefined,則表示要從結(jié)果中刪除相應(yīng)的鍵馁痴;如果返回其他值谊娇,則將該值插入到結(jié)果中。