JSON解析與序列化


JSON之所以流行,擁有與JavaScript類似的語法並不是全部原因。更重要的一個原因是,可以把JSON數據結構解析為有用的 JavaScript對象。與XML數據結構要解析成DOM文檔而且從中提取數據極為麻煩相比,JSON可以解析為JavaScript對象的優勢極其明 顯。

JSON對象

早期的JSON解析器基本上就是使用JavaScript的eval()函數。由於JSON是JavaScript語法的子集,因此eval()函 數可以解析、解釋並返回JavaScript對象和數組。ECMAScript 5對解析Json的行為進行規范,定義了全局對象JSON。支持這個對象的瀏覽器有IE8+、Firefox 3.5+、Safari4+、Chrome和Opera10.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,該方法會拋出錯誤。

JSON序列化選項

實際上,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}

如果第二個參數是函數,行為會稍有不同。傳入的函數接收兩個參數,屬性(鍵)名和屬性值。根據屬性(鍵)名可以知道應該如何處理要序列化的對象中的屬性。屬性名只能是字符串,而在值並非鍵值對兒結構的值時,鍵名可以是空字符串。

為了改變序列化對象的結果,函數返回的值就是相應鍵的值。不過要注意,如果函數返回了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;     } }); alert(jsonText);

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

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

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

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 }

SON.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對象自動轉換成ISO8601日 期字符串(與在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()方法,還是考慮使用函數過濾器,亦或需要同時使用兩者,理解這個順序都是至關重要的。

JSON解析選項

JSON.parse()方法也可以接收另一個參數,該參數是一個函數,將在每個鍵值對兒上調用。為了區別JSON.stringify()接收的 替換(過濾)函數,這個函數被稱為還原函數(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()方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM