一、介紹
這節(jié)課呢,我們來了解的是JavaScript JSON解析與序列化,
JSON之所以流行是因?yàn)榭梢园袹SON數(shù)據(jù)結(jié)構(gòu)解析為有用的JavaScript對(duì)象。
早期的JSON解析器基本上就是使用JavaScript的eval()函數(shù),但是由于使用eval()對(duì)JSON數(shù)據(jù)結(jié)構(gòu)求值存在風(fēng)險(xiǎn),因?yàn)榭赡軙?huì)執(zhí)行一些惡意代碼,所以ECMAscript5對(duì)解析JSON的行為進(jìn)行了規(guī)范,定義了全局對(duì)象JSON。
JSON對(duì)象有兩個(gè)方法:stringify()和parse()。在最簡(jiǎn)單的情況下,這兩個(gè)方法分別用于把JavaScript對(duì)象序列化為JSON字符串和JSON字符串解析為原生JavaScript值。
二、知識(shí)點(diǎn)介紹
1、JSON對(duì)象
2、JSON.stringify()序列化選項(xiàng)
3、JavaScript 對(duì)象轉(zhuǎn)換
4、JavaScript數(shù)組轉(zhuǎn)換
5、異常
6、JSON序列化選項(xiàng)
7、JSON.parse()解析選項(xiàng)
8、解析實(shí)例
三、上課對(duì)應(yīng)視頻的說明文檔
1、JSON對(duì)象
早期的JSON解析器基本上就是使用JavaScript的eval()函數(shù)。由于JSON是JavaScript語(yǔ)法的子集,因此eval()函 數(shù)可以解析、解釋并返回JavaScript對(duì)象和數(shù)組。ECMAScript 5對(duì)解析Json的行為進(jìn)行規(guī)范,定義了全局對(duì)象JSON。支持這個(gè)對(duì)象的瀏覽器有IE8+、Firefox 3.5+、Safari4+、Chrome和Opera10.5+。對(duì)于較早版本的瀏覽器可以使用一個(gè)shim:https://github.com /douglascrockford/JSON-js。在舊版本的瀏覽器中,使用eval()對(duì)JSON數(shù)據(jù)結(jié)構(gòu)求值存在風(fēng)險(xiǎn),因?yàn)榭赡軙?huì)執(zhí)行一些惡意代 碼。對(duì)于不能原生支持JSON解析的瀏覽器,使用這個(gè)shim是最佳的選擇。
JSON對(duì)象有兩個(gè)方法:stringify()和parse()。在最簡(jiǎn)單的情況下,這兩個(gè)方法分別用于把JavaScript對(duì)象序列化為JSON字符串和把JSON字符串解析為原生JavaScript值。例如:
var book = {
title: "Professional JavaScript",
authors: ["NIcholas C. Zakas"],
edition: 3,
year: 2011};var jsonText = JSON.stringify(book);
這個(gè)例子使用JSON.stringify()把一個(gè)JavaScript對(duì)象序列化為一個(gè)JSON字符串,然后將它保存在變量jsonText 中。默認(rèn)情況下,JSON.stringify()輸出的JSON字符串不包含任何空格字符或縮進(jìn),因此保存在jsonText中的字符串如下所示:
{"title":"Professional JavaScript","authors":["NIcholas C. Zakas"],"edition":3,"year":2011}
在序列化JavaScript對(duì)象時(shí),所有函數(shù)及原型成員都會(huì)被有意忽略,不體現(xiàn)在結(jié)果中。此時(shí),值為undefined的任何屬性也都會(huì)被跳過。結(jié)果中最終都是值為有效JSON數(shù)據(jù)類型的實(shí)例屬性。
將JSON字符串直接傳遞給JSON.parse()就可以得到相應(yīng)的JavaScript值。例如,使用下列代碼就可以創(chuàng)建與book類似的對(duì)象:
var bookCopy = JSON.parse(jsonText);
注意,雖然book與bookCopy具有相同的屬性,但它們是兩個(gè)對(duì)立的、沒有任何關(guān)系的對(duì)象。
如果傳給JSON.parse()的字符串不是有效的JSON,該方法會(huì)拋出錯(cuò)誤。
2、JSON.stringify()序列化選項(xiàng)
JSON 通常用于與服務(wù)端交換數(shù)據(jù)。
在向服務(wù)器發(fā)送數(shù)據(jù)時(shí)一般是字符串。
我們可以使用 JSON.stringify() 方法將 JavaScript 對(duì)象轉(zhuǎn)換為字符串。 語(yǔ)法
JSON.stringify(value[, replacer[, space]])
參數(shù)說明:
value:必需, 一個(gè)有效的 JSON 對(duì)象。
replacer: 可選。用于轉(zhuǎn)換結(jié)果的函數(shù)或數(shù)組。
如果 replacer 為函數(shù),則 JSON.stringify 將調(diào)用該函數(shù),并傳入每個(gè)成員的鍵和值。使用返回值而不是原始值。如果此函數(shù)返回 undefined,則排除成員。根對(duì)象的鍵是一個(gè)空字符串:""。
如果 replacer 是一個(gè)數(shù)組,則僅轉(zhuǎn)換該數(shù)組中具有鍵值的成員。成員的轉(zhuǎn)換順序與鍵在數(shù)組中的順序一樣。當(dāng) value 參數(shù)也為數(shù)組時(shí),將忽略 replacer 數(shù)組。
space: 可選,文本添加縮進(jìn)、空格和換行符,如果 space 是一個(gè)數(shù)字,則返回值文本在每個(gè)級(jí)別縮進(jìn)指定數(shù)目的空格,如果 space 大于 10,則文本縮進(jìn) 10 個(gè)空格。space 有可以使用非數(shù)字,如:\t。
3、JavaScript 對(duì)象轉(zhuǎn)換
例如我們向服務(wù)器發(fā)送以下數(shù)據(jù):
var obj = { "name":"runoob", "alexa":10000,
"site":"www.runoob.com"};
我們使用 JSON.stringify() 方法處理以上數(shù)據(jù),將其轉(zhuǎn)換為字符串:
var myJSON = JSON.stringify(obj);
myJSON 為字符串。
我們可以將 myJSON 發(fā)送到服務(wù)器:
實(shí)例
var obj = { "name":"runoob", "alexa":10000, "site":"www.runoob.com"};? var myJSON = JSON.stringify(obj);?
document.getElementById("demo").innerHTML = myJSON;
4、JavaScript 數(shù)組轉(zhuǎn)換
我們也可以將 JavaScript 數(shù)組轉(zhuǎn)換為 JSON 字符串:
實(shí)例
var arr = [ "Google", "Runoob", "Taobao", "Facebook" ];
var myJSON = JSON.stringify(arr);
myJSON 為字符串。
我們可以將 myJSON 發(fā)送到服務(wù)器:
實(shí)例
var arr = [ "Google", "Runoob", "Taobao", "Facebook" ]; var myJSON = JSON.stringify(arr);
document.getElementById("demo").innerHTML = myJSON;
5、異常
5.1、解析數(shù)據(jù)
JSON 不能存儲(chǔ) Date 對(duì)象。
如果你需要存儲(chǔ) Date 對(duì)象,需要將其轉(zhuǎn)換為字符串。
JSON.stringify() 會(huì)將所有日期轉(zhuǎn)換為字符串。
之后再將字符串轉(zhuǎn)換為 Date 對(duì)象。
實(shí)例
var obj = { "name":"Runoob", "initDate":new Date(),
"site":"www.runoob.com"};
var myJSON = JSON.stringify(obj);
document.getElementById("demo").innerHTML = myJSON;
之后你可以再將字符串轉(zhuǎn)換為 Date 對(duì)象。
實(shí)例
var text = '{ "name":"Runoob", "initDate":"2013-12-14", "site":"www.runoob.com"}';
var obj = JSON.parse(text);
obj.initDate = new Date(obj.initDate);
document.getElementById("demo").innerHTML = obj.name + "創(chuàng)建日期: " + obj.initDate;
我們可以啟用 JSON.parse 的第二個(gè)參數(shù) reviver,一個(gè)轉(zhuǎn)換結(jié)果的函數(shù),對(duì)象的每個(gè)成員調(diào)用此函數(shù)。
實(shí)例
var text = '{ "name":"Runoob", "initDate":"2013-12-14", "site":"www.runoob.com"}';
var obj = JSON.parse(text, function (key, value) {
if (key == "initDate") {
return new Date(value);
} else {
return value;
}
});
document.getElementById("demo").innerHTML = obj.name + "創(chuàng)建日期:" + obj.initDate;
5.2、解析函數(shù)
JSON 不允許包含函數(shù),JSON.stringify() 會(huì)刪除 JavaScript 對(duì)象的函數(shù),包括 key 和 value。
實(shí)例
var obj = { "name":"Runoob", "alexa":function () {return 10000;}, "site":"www.runoob.com"};
var myJSON = JSON.stringify(obj);
document.getElementById("demo").innerHTML = myJSON;
我們可以在執(zhí)行 JSON.stringify() 函數(shù)前將函數(shù)轉(zhuǎn)換為字符串來避免以上問題的發(fā)生:
實(shí)例
var text = '{ "name":"Runoob", "alexa":"function () {return 10000;}", "site":"www.runoob.com"}';
var obj = JSON.parse(text); obj.alexa = eval("(" + obj.alexa + ")");
document.getElementById("demo").innerHTML = obj.name + " Alexa 排名:" + obj.alexa();
實(shí)例
var obj = { "name":"Runoob", "alexa":function () {return 10000;}, "site":"www.runoob.com"};
obj.alexa = obj.alexa.toString();
var myJSON = JSON.stringify(obj);
document.getElementById("demo").innerHTML = myJSON;
不建議在 JSON 中使用函數(shù)。
6、JSON序列化選項(xiàng)
實(shí)際上,JSON.stringify()除了要序列化的JavaScript對(duì)象外,還可以接收另外兩個(gè)參數(shù),這兩個(gè)參數(shù)用于指定以下不同的方式 序列化JavaScript對(duì)象。第一個(gè)參數(shù)是個(gè)過濾器,可以是一個(gè)數(shù)組,也可以是一個(gè)函數(shù);第二個(gè)參數(shù)是一個(gè)選項(xiàng),表示是否在JSON字符串中保留縮 進(jìn)。單獨(dú)或組合使用這兩個(gè)參數(shù),可以更全面深入地控制JSON的序列化。
6.1、過濾結(jié)果
如果過濾器參數(shù)是數(shù)組,那么JSON.stringify()結(jié)果中將只包含數(shù)組中列出的屬性。來看下面的例子。
var book = {
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"],
edition: 3,
year: 2011};var jsonText = JSON.stringify(book, ["title", "edition"]);
JSON.stringify()的第二個(gè)參數(shù)是一個(gè)數(shù)組,其中包含兩個(gè)字符串:“title”和“edition”。這個(gè)屬性將要序列化的對(duì)象中的屬性是對(duì)應(yīng)的,因此在返回的結(jié)果字符串中,就只會(huì)包含這兩個(gè)屬性:
{"title":"Professional JavaScript", "edition":3}
如果第二個(gè)參數(shù)是函數(shù),行為會(huì)稍有不同。傳入的函數(shù)接收兩個(gè)參數(shù),屬性(鍵)名和屬性值。根據(jù)屬性(鍵)名可以知道應(yīng)該如何處理要序列化的對(duì)象中的屬性。屬性名只能是字符串,而在值并非鍵值對(duì)兒結(jié)構(gòu)的值時(shí),鍵名可以是空字符串。
為了改變序列化對(duì)象的結(jié)果,函數(shù)返回的值就是相應(yīng)鍵的值。不過要注意,如果函數(shù)返回了undefined,那么相應(yīng)的屬性會(huì)被忽略。還是看一個(gè)例子吧。
var book = {
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"],
edition: 3,
year: 2011};var jsonText = JSON.stringify(book, function (key, value) {
switch (key) {
case "authors":
return value.join(",")
case "year":
return 5000;
case "edition":
return undefined;
default:
return value;
}});
alert(jsonText);
這里,函數(shù)過濾器根據(jù)傳入的鍵來決定結(jié)果。如果鍵為“authors”,就將數(shù)組連接為一個(gè)字符串;如果鍵為“year”,則將其值設(shè)置為 5000;如果鍵為“edition”,通過返回undefined刪除該屬性。最后,一定要提供default項(xiàng),此時(shí)返回傳入的值,以便其它值都能正 常出現(xiàn)在結(jié)果中。實(shí)際上,第一次調(diào)用這個(gè)函數(shù)過濾器,傳入的鍵是一個(gè)空字符串,而值就是book對(duì)象。序列化后的JSON字符串如下所示:
{"title":"Professional JavaScript","authors":"Nicholas C. Zakas","year":5000}
要序列化的對(duì)象中的每一個(gè)對(duì)象都要經(jīng)過過濾器,因此數(shù)組中的每個(gè)帶有這些屬性的對(duì)象經(jīng)過過濾之后,每個(gè)對(duì)象都只會(huì)包含“title”、“authors”和“year”屬性。
6.2、字符串縮進(jìn)
JSON.stringify()方法的第三個(gè)參數(shù)用于控制結(jié)果中的縮進(jìn)和空白符。如果這個(gè)參數(shù)是一個(gè)數(shù)值,那它表示的是每個(gè)級(jí)別縮進(jìn)的空格數(shù)。例如,要在每個(gè)級(jí)別縮進(jìn)4個(gè)空格,可以這樣寫代碼:
var book = {
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"],
edition: 3,
year: 2011};var jsonText = JSON.stringify(book, null, 4);
保存在jsonText中的字符串如下所示:
{
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"],
"edition": 3,
"year": 2011}
SON.stringify()也在結(jié)果字符串中插入換行符以提高可讀性。只要傳入有效的控制縮進(jìn)的參數(shù)值,結(jié)果字符串就會(huì)包含換行符。最大縮進(jìn)空格數(shù)為10,所有大于10的值會(huì)自動(dòng)轉(zhuǎn)換為10。
如果縮進(jìn)參數(shù)是一個(gè)字符串而非數(shù)值,則這個(gè)字符串將在JSON字符串中被用作縮進(jìn)字符。在使用字符串的情況下,可以將縮進(jìn)字符設(shè)置為制表符,或者兩個(gè)短劃線之類的任意字符。
var jsonText = JSON.stringify(book, null, " -- ");
這樣,jsonText中的字符串將變成如下所示:
{
--"title": "Professional JavaScript",
--"authors": [----"Nicholas C. Zakas"--],
--"edition": 3,
--"year": 2011}
縮進(jìn)字符串最長(zhǎng)不能超過10個(gè)字符長(zhǎng)。如果字符串長(zhǎng)度超過了10個(gè),結(jié)果中只出現(xiàn)10個(gè)字符。
6.3、toJSON()方法
有時(shí)候,JSON.stringify()還是不能滿足對(duì)某些對(duì)象進(jìn)行自定義序列化的需求。在這些情況下,可以通過對(duì)象上調(diào)用toJSON()方 法,返回其自身的JSON數(shù)據(jù)格式。原生Date對(duì)象有一個(gè)toJSON()方法,能夠?qū)avaScript的Date對(duì)象自動(dòng)轉(zhuǎn)換成ISO8601日 期字符串(與在Date對(duì)象上調(diào)用toISOString()的結(jié)果完全一樣)。
可以為任何對(duì)象添加toJSON()方法,比如:
var book = {
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"],
edition: 3,
year: 2011,
toJSON: function () {
return this.title;
}};var jsonText = JSON.stringify(book);
以上代碼在book對(duì)象上定義了一個(gè)toJSON()方法,該方法返回圖書的書名。與Date對(duì)象類似,這個(gè)對(duì)象也將被序列化為一個(gè)簡(jiǎn)單的字符串而 非對(duì)象??梢宰宼oJSON()方法返回任何序列化的值,它都能正常工作。也可以讓這個(gè)方法返回undefined,此時(shí)如果包含它的對(duì)象嵌入在另一個(gè)對(duì) 象中,會(huì)導(dǎo)致該對(duì)象的值變成null,而如果包含它的對(duì)象是頂級(jí)對(duì)象,結(jié)果就是undefined。
toJSON()可以作為函數(shù)過濾器的補(bǔ)充,因此理解序列化的內(nèi)部順序十分重要。假設(shè)把一個(gè)對(duì)象傳入JSON.stringify(),序列化該對(duì)象的順序如下。
1)如果存在toJSON()方法而且能通過它取得有效的值,則調(diào)用該方法。否則,按默認(rèn)順序執(zhí)行序列化。
2)如果提供了第二個(gè)參數(shù),應(yīng)用這個(gè)函數(shù)過濾器。傳入函數(shù)過濾器的值是第(1)步返回的值。
3)對(duì)第(2)步返回的每個(gè)值進(jìn)行相應(yīng)的序列化。
4)如果提供了第三個(gè)參數(shù),執(zhí)行相應(yīng)的格式化。
無論是考慮定義toJSON()方法,還是考慮使用函數(shù)過濾器,亦或需要同時(shí)使用兩者,理解這個(gè)順序都是至關(guān)重要的。
7、JSON.parse()解析選項(xiàng)
JSON 通常用于與服務(wù)端交換數(shù)據(jù)。
在接收服務(wù)器數(shù)據(jù)時(shí)一般是字符串。
我們可以使用 JSON.parse() 方法將數(shù)據(jù)轉(zhuǎn)換為 JavaScript 對(duì)象。
語(yǔ)法
JSON.parse(text[, reviver])
參數(shù)說明:
?text:必需, 一個(gè)有效的 JSON 字符串。
?reviver: 可選,一個(gè)轉(zhuǎn)換結(jié)果的函數(shù), 將為對(duì)象的每個(gè)成員調(diào)用此函數(shù)。
JSON.parse()方法也可以接收另一個(gè)參數(shù),該參數(shù)是一個(gè)函數(shù),將在每個(gè)鍵值對(duì)兒上調(diào)用。為了區(qū)別JSON.stringify()接收的 替換(過濾)函數(shù),這個(gè)函數(shù)被稱為還原函數(shù)(reviver),但實(shí)際上這兩個(gè)函數(shù)的簽名是相同的——它們都接收兩個(gè)參數(shù),一個(gè)鍵和一個(gè)值,而且都需要返 回一個(gè)值。
如果還原函數(shù)返回undefined,則表示要從結(jié)果中刪除相應(yīng)的鍵;如果返回其它值,則將該值插入到結(jié)果中。在將日期字符串轉(zhuǎn)換為Date對(duì)象時(shí),經(jīng)常要用到還原函數(shù)。例如:
var book = {
"title": "Professional JavaScript",
"authors": ["Nicholas C. Zakas"],
edition: 3,
year: 2011,
releaseDate: new Date(2011, 11, 1)};var jsonText = JSON.stringify(book);
var bookCopy = JSON.parse(jsonText, function (key, value) {
if (key == "releaseDate") {
return new Date(value);
} else {
return value;
}});
alert(bookCopy.releaseDate.getFullYear());
以上代碼先是為book對(duì)象新增了一個(gè)releaseDate屬性,該屬性保存著一個(gè)Date對(duì)象。這個(gè)對(duì)象在經(jīng)過序列化之后變成了有效的JSON 字符串,然后經(jīng)過解析又在bookCopy中還原為一個(gè)Date對(duì)象。還原函數(shù)在遇到”releaseDate”鍵時(shí),會(huì)基于相應(yīng)的值創(chuàng)建一個(gè)新的 Date對(duì)象。結(jié)果就是bookCopy.releaseDate屬性中會(huì)保存一個(gè)Date對(duì)象。正是因?yàn)槿绱耍拍芑谶@個(gè)對(duì)象調(diào)用 getFullYear()方法。
8、JSON 解析實(shí)例
例如我們從服務(wù)器接收了以下數(shù)據(jù):
{ "name":"runoob", "alexa":10000, "site":"www.runoob.com" }
我們使用 JSON.parse() 方法處理以上數(shù)據(jù),將其轉(zhuǎn)換為 JavaScript 對(duì)象:
var obj = JSON.parse('{ "name":"runoob", "alexa":10000, "site":"www.runoob.com" }');
解析前要確保你的數(shù)據(jù)是標(biāo)準(zhǔn)的 JSON 格式,否則會(huì)解析出錯(cuò)。
你可以使用我們的在線工具檢測(cè):https://c.runoob.com/front-end/53。
解析完成后,我們就可以在網(wǎng)頁(yè)上使用 JSON 數(shù)據(jù)了:
實(shí)例
<p id="demo"></p>
<script>
var obj = JSON.parse('{ "name":"runoob", "alexa":10000, "site":"www.runoob.com" }');
document.getElementById("demo").innerHTML = obj.name + ":" + obj.site;
</script>
從服務(wù)端接收 JSON 數(shù)據(jù)
我們可以使用 AJAX 從服務(wù)器請(qǐng)求 JSON 數(shù)據(jù),并解析為 JavaScript 對(duì)象。
實(shí)例
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myObj = JSON.parse(this.responseText);
document.getElementById("demo").innerHTML = myObj.name;
}
};
xmlhttp.open("GET", "/try/ajax/json_demo.txt", true);
xmlhttp.send();
從服務(wù)端接收數(shù)組的 JSON 數(shù)據(jù)
如果從服務(wù)端接收的是數(shù)組的 JSON 數(shù)據(jù),則 JSON.parse 會(huì)將其轉(zhuǎn)換為 JavaScript 數(shù)組:
實(shí)例
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myArr = J? SON.parse(this.responseText);?
document.getElementById("demo").innerHTML = myArr[1];
}
};
xmlhttp.open("GET", "/try/ajax/json_demo_array.txt", true);? ?
xmlhttp.send();