20.2 解析與序列化【JavaScript進階程式設計第三版】

來源:互聯網
上載者:User

標籤:cti   firefox   opera   tps   支援   層級   結構   資料   roc   

JSON 之所以流行,擁有與JavaScript 類似的文法並不是全部原因。更重要的一個原因是,可以把JSON 資料結構解析為有用的JavaScript 對象。與XML 資料結構要解析成DOM 文檔而且從中提取資料極為麻煩相比,JSON 可以解析為JavaScript 對象的優勢極其明顯。就以上一節中包含一組圖書的JSON資料結構為例,在解析為JavaScript 對象後,只需要下面一行簡單的代碼就可以取得第三本書的書名:

books[2].title

當然,這裡是假設把解析JSON 資料結構後得到的對象儲存到了變數books 中。再看看下面在DOM結構中尋找資料的代碼:

doc.getElementsByTagName("book")[2].getAttribute("title")

看看這些多餘的方法調用,就不難理解為什麼JSON 能得到JavaScript 開發人員的熱烈歡迎了。從此以後,JSON 就成了Web 服務開發中交換資料的事實標準。

20.2.1 JSON對象

早期的JSON 解析器基本上就是使用JavaScript 的eval()函數。由於JSON 是JavaScript 文法的子集,因此eval()函數可以解析、解釋並返回JavaScript 對象和數組。ECMAScript 5 對解析JSON 的行為進行規範,定義了全域對象JSON。支援這個對象的瀏覽器有IE 8+、Firefox 3.5+、Safari 4+、Chrome和Opera 10.5+。對於較早版本的瀏覽器,可以使用一個shim:https://github.com/douglascrockford/JSON-js。在舊版本的瀏覽器中,使用eval()對JSON 資料結構求值存在風險,因為可能會執行一些惡意代碼。對於不能原生支援JSON 解析的瀏覽器,使用這個shim 是最佳選擇。

JSON 對象有兩個方法:stringify()和parse()。在最簡單的情況下,這兩個方法分別用於把JavaScript 對象序列化為JSON 字串和把JSON 字串解析為原生JavaScript 值。例如:

var book = {title: "Professional JavaScript",authors: ["Nicholas C. Zakas"],edition: 3,year: 2011};var jsonText = JSON.stringify(book);

運行一下
這個例子使用JSON.stringify()把一個JavaScript 對象序列化為一個JSON 字串,然後將它儲存在變數jsonText 中。預設情況下,JSON.stringify()輸出的JSON 字串不包含任何空白字元或縮排,因此儲存在jsonText 中的字串如下所示:

{"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],"edition": 3,"year": 2011}

在序列化JavaScript 對象時,所有函數及原型成員都會被有意忽略,不體現在結果中。此外,值為undefined 的任何屬性也都會被跳過。結果中最終都是值為有效JSON 資料類型的執行個體屬性。
將JSON 字串直接傳遞給JSON.parse()就可以得到相應的JavaScript 值。例如,使用下列代碼就可以建立與book 類似的對象:

var bookCopy = JSON.parse(jsonText);

注意,雖然book 與bookCopy 具有相同的屬性,但它們是兩個獨立的、沒有任何關係的對象。
如果傳給JSON.parse()的字串不是有效JSON,該方法會拋出錯誤。

20.2.2 序列化選項

實際上,JSON.stringify()除了要序列化的JavaScript 對象外,還可以接收另外兩個參數,這兩個參數用於指定以不同的方式序列化JavaScript 對象。第一個參數是個過濾器,可以是一個數組,也可以是一個函數;第二個參數是一個選項,表示是否在JSON 字串中保留縮排。單獨或組合使用這兩個參數,可以更全面深入地控制JSON 的序列化。

1. 過濾結果

如果過濾器參數是數組,那麼JSON.stringify()的結果中將只包含數組中列出的屬性。來看下面的例子。

var book = {"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],edition: 3,year: 2011};var jsonText = JSON.stringify(book, ["title", "edition"]);

運行一下
JSON.stringify()的第二個參數是一個數組,其中包含兩個字串:"title"和"edition"。這兩個屬性與將要序列化的對象中的屬性是對應的,因此在返回的結果字串中,就只會包含這兩個屬性:

{"title":"Professional JavaScript","edition":3}

如果第二個參數是函數,行為會稍有不同。傳入的函數接收兩個參數,屬性(鍵)名和屬性值。根據屬性(鍵)名可以知道應該如何處理要序列化的對象中的屬性。屬性名稱只能是字串,而在值並非索引值對兒結構的值時,鍵名可以是Null 字元串。
為了改變序列化對象的結果,函數返回的值就是相應鍵的值。不過要注意,如果函數返回了undefined,那麼相應的屬性會被忽略。還是看一個例子吧。

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;}});

運行一下
這裡,函數過濾器根據傳入的鍵來決定結果。如果鍵為"authors",就將數組串連為一個字串;如果鍵為"year",則將其值設定為5000;如果鍵為"edition",通過返回undefined 刪除該屬性。
最後,一定要提供default 項,此時返回傳入的值,以便其他值都能正常出現在結果中。實際上,第一次調用這個函數過濾器,傳入的鍵是一個Null 字元串,而值就是book 對象。序列化後的JSON 字串如下所示:

{"title":"Professional JavaScript","authors":"Nicholas C. Zakas","year":5000}

要序列化的對象中的每一個對象都要經過過濾器,因此數組中的每個帶有這些屬性的對象經過過濾之後,每個對象都只會包含"title"、"authors"和"year"屬性。

Firefox 3.5 和3.6 對JSON.stringify()的實現有一個bug,在將函數作為該方法的第二個參數時這個bug 就會出現,即這個函數只能作為過濾器:返回undefined 意味著要跳過某個屬性,而返回其他任何值都會在結果中包含相應的屬性。Firefox 4 修複了這個bug。
2. 字串縮排

JSON.stringify()方法的第三個參數用於控制結果中的縮排和空白符。如果這個參數是一個數值,那它表示的是每個層級縮排的空格數。例如,要在每個層級縮排4 個空格,可以這樣寫代碼:

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}

不知道讀者注意到沒有,JSON.stringify()也在結果字串中插入了分行符號以提高可讀性。只要傳入有效控制縮排的參數值,結果字串就會包含分行符號。(只縮排而不換行意義不大。)最大縮排空格數為10,所有大於10 的值都會自動轉換為10。
如果縮排參數是一個字串而非數值,則這個字串將在JSON 字串中被用作縮排字元(不再使用空格)。在使用字串的情況下,可以將縮排字元設定為定位字元,或者兩個短劃線之類的任一字元。

var jsonText = JSON.stringify(book, null, " - -");

這樣,jsonText 中的字串將變成如下所示:

{--"title": "Professional JavaScript",--"authors": [----"Nicholas C. Zakas"--],--"edition": 3,--"year": 2011}

縮排字串最長不能超過10 個字元長。如果字串長度超過了10 個,結果中將只出現前10 個字元。

3. toJSON()方法

有時候,JSON.stringify()還是不能滿足對某些對象進行自訂序列化的需求。在這些情況下,可以給對象定義toJSON()方法,返回其自身的JSON 資料格式。原生Date 對象有一個toJSON()方法,能夠將JavaScript 的Date 對象自動轉換成ISO 8601 日期文字(與在Date 對象上調用toISOString()的結果完全一樣)。
可以為任何對象添加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 對象上定義了一個toJSON()方法,該方法返回圖書的書名。與Date 對象類似,這個對象也將被序列化為一個簡單的字串而非對象。可以讓toJSON()方法返回任何值,它都能正常工作。比如,可以讓這個方法返回undefined,此時如果包含它的對象嵌入在另一個對象中,會導致它的值變成null,而如果它是頂級對象,結果就是undefined。
toJSON()可以作為函數過濾器的補充,因此理解序列化的內部順序十分重要。假設把一個對象傳入JSON.stringify(),序列化該對象的順序如下。
(1) 如果存在toJSON()方法而且能通過它取得有效值,則調用該方法。否則,返回對象本身。
(2) 如果提供了第二個參數,應用這個函數過濾器。傳入函數過濾器的值是第(1)步返回的值。
(3) 對第(2)步返回的每個值進行相應的序列化。
(4) 如果提供了第三個參數,執行相應的格式化。
無論是考慮定義toJSON()方法,還是考慮使用函數過濾器,亦或需要同時使用兩者,理解這個順序都是至關重要的。

20.2.3 解析選項

JSON.parse()方法也可以接收另一個參數,該參數是一個函數,將在每個索引值對兒上調用。為了區別JSON.stringify()接收的替換(過濾)函數(replacer),這個函數被稱為還原函數(reviver),但實際上這兩個函數的簽名是相同的——它們都接收兩個參數,一個鍵和一個值,而且都需要返回一個值。
如果還原函數返回undefined,則表示要從結果中刪除相應的鍵;如果返回其他值,則將該值插入到結果中。在將日期文字轉換為Date 對象時,經常要用到還原函數。例如:

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 對象新增了一個releaseDate 屬性,該屬性儲存著一個Date 對象。這個對象在經過序列化之後變成了有效JSON 字串,然後經過解析又在bookCopy 中還原為一個Date對象。還原函數在遇到"releaseDate"鍵時,會基於相應的值建立一個新的Date 對象。結果就是bookCopy.releaseDate 屬性中會儲存一個Date 對象。正因為如此,才能基於這個對象調用getFullYear()方法。

更多章節教程:http://www.shouce.ren/api/view/a/15218

20.2 解析與序列化【JavaScript進階程式設計第三版】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.