一、序列化與反序列化的概念
序列化(Serialization):將數據結構或是對象 轉換為 二進制串(字節序列)的過程
反序列化:將二進制串(字節序列)轉換為 數據結構或者對象 的過程
序列化 就是將對象的狀態信息轉換為可以存儲或傳輸的形式的過程。在序列化期間,對象將其當前狀態寫入到臨時或持久性存儲區(如硬盤)。以后,可以通過從存儲區中讀取或反序列化對象的狀態,重新創建該對象。
二、對象序列化的用途目的:
1、把對象的字節序列永久保存在硬盤上(以某種儲存方式使自定義對象持久化);
2、在網絡上傳送對象的二進制序列(將對象從一個地方傳遞到另一個地方);
3、使程序更具維護性。
三、解析與序列化
早期的JSON解析器使用的是JavaScript函數的eval( ) 函數。由於JSON是JS語法的子集,所以eval()函數可以解析、解釋並返回JS對象和數組。
由於在一些較早版本的瀏覽器,使用eval()對JSON數據結構存在風險,可能會產生一些惡意代碼,所以現在不再經常使用這個函數。
JSON對象有兩個方法:(在最簡單的情況下)
JSON.stringify() 把JavaScript對象序列轉換為JSON字符串;
JSON.parse() 將JSON字符串解析為原生Javascript對象;
實例:
在這個例子中使用JSON.stringify( ) 把JavaScript對象序列轉換為一個JSON字符串
然后將其保存在jsonText變量中,並使用document.write( ) 函數使結果在頁面輸出。
var person= { "name":"張三", "address":["中國","河北"], "age":"20", "gender":"男" , "birth":"1999" }; var jsonText = JSON.stringify(person);
document.write(jsonText);
顯示結果

注意:在序列化JavaScript對象時,所有的函數及原形成員都會被有意忽略,默認情況下JSON.stringify()輸出的JSON字符串不包含任何空格字符或縮進;,不體現在結果中。此外,值為Undefined的任何屬性也都會被跳過。最終結果中都是值為有效JSON數據類型的實例屬性。
2、將JSON字符串直接傳遞給JSON.parse( ) 就可以得到相應的JavaScript值
例如,使用以下代碼就會創建於person類似的對象。

注意:雖然person和persoinf 具有相同的屬性,但是它們是兩個獨立的、沒有任何關系的對象。如果傳遞給 JSON.parse()不是有效的JSON,該方法就會出錯。
四、序列化選項
實際上,JSON.stringify()除了要序列化的javascript對象之外,還可以接收兩個參數,這兩個參數可以用於指定以不同方式序列化的JavaScript
1、第一個參數是過濾器(可為數組或函數)
2、第二個參數是一個選項(表示是否在JSON字符串中保留縮進)
(一)過濾結果
1、如果過濾器的參數是數組,那么JSON.stringify( ) 的結果中將只包含數組中列出的屬性
例如:
var person= { "name":"張三 李四", "address":["中國","河北"], "age":20, "gender":"男" , "birth":"1999" }; var jsonText = JSON.stringify(person,["name","age"]); document.write(jsonText);
JSON.stringify( ) 的第一個參數是變量名稱,第二個參數是一個數組,其中包含兩個字符串:"name" 和 "age" 。這兩個屬性與將要序列化的對象中的屬性是對應的,因此在返回結果的字符串中就會包含這兩個屬性:

2、如果第二個參數是函數,行為會稍有些不同。傳入的函數接受兩個參數,屬性(鍵)名和屬性值。根據屬性名(鍵)可以知道應該如何處理要序列化的對象的屬性。
屬性名只能是字符串,而在值並非鍵值對結構的值時,鍵名可以是空字符串。
為了改變序列化對象的結果,函數返回的值就是相應鍵的值。
注意:如果函數返回了undefined,那么相應的屬性會被忽略
var person= { "name":"張三", "address":["中國 河北"], "age":20, "gender":"男" , "birth":1999 }; var jsonText = JSON.stringify(person, function(key, value) { switch(key){ case "address": return value.join(",") case "birth": return 2000; case "age": return undefined; default: return value; } }); document.write(jsonText);
函數過濾器根據傳入的鍵來決定結果。
如果鍵(屬性名)為 "adress",則將數組連接為一個字符串;
如果鍵為 "birth",則將其值設置為2000;
如果鍵為 "age",通過返回undefined來刪除該屬性。
最后,一定要提供default項,此時返回傳入的值,以便其他的值都可以正常出現在結果中。實際上,第一次調用這個函數過濾器,傳入的鍵是一個空字符串,而值就是person 對象。
序列化后的字符串如下所示:

要序列化的對象中每一個對象都要經過過濾器,因此數組中的每個帶都有這些屬性的對象經過過濾器之后,每個對象都會包含"name"、"address"、"gender"、"birth" 屬性。
(二)字符串縮進
JSON.stringify() 方法的第三個參數用於控制結果中的縮進和空白字符。
如果這個參數是一個數值,那他表示的是每個級別縮進的空格數。
實例:
1、要在每個級別中縮進5個空格
var person= { "name":"張三", "address":["中國 河北"], "age":20, "gender":"男" , "birth":1999 }; var jsonText = JSON.stringify(person, null,5); document.write(jsonText);
保存在jsonText中的字符串如下:

注意:JSON.stringify()也在結果字符中穿插了換行符以提高可讀性。只要傳入有效的控制縮進的參數值。最大縮進空格數為10,所有大於10的值都會自動轉換為10。
2、如果縮進參數是一個字符串而並非數值,則這個字符串將在JSON中被用作縮進符(不再使用空格)。
在使用字符串的情況下,可以將縮進字符設置為制表符,或是兩個短線之類任意字符。
注意:縮進符最長不超過10個字符。如果超過了10個字符長,結果中就會只顯示出前10個的字符




(三)toJSON( ) 方法
有時候JSON.stringify() 方法不能滿足對某些對象自定義序列化的需求。
這時,可以給對象定義toJSON( ) 方法,返回其自身的JSON數據格式。
原生Date對象有一個toJSON()方法,能將JavaScript的Date對象自動轉換為ISO 8601日期字符串(與在Date對象上調用toISOString()的結果完全一樣)
可以為任何對象添加 toJSON( ) 方法。
實例:
var person= { "name":"張三", "address":["中國 河北"], "age":20, "gender":"男" , "birth":1999, toJSON: function(){ return person.address; // return this.address; } }; var jsonText = JSON.stringify(person); console.log(jsonText);

person對象上定義了一個toJSON( ) 方法,該方法返回 person 的 address 地址。這個對象可以被序列化為一個簡單的字符串而非對象。
可以讓toJSON()方法返回任何值,都可以正常工作。
假設,想讓方法返回 undefined ,此時如果包含它的對象嵌套在另一個對象中,會導致它的值變成null,如果是頂級對象,則結果就是undefined。
(四)序列化對象的順序
假設把一個對象傳入JSON.stringify( ),序列化該對象的順序如下:
1、如果存在 toJSON( ) 方法並且可以通過方法獲得有效的值,則調用該方法,否則返回對象本身;
2、如果提供了第二個參數,應用這個函數過渡器。傳入函數過渡器的值是第一步返回的值;
3、對第二步返回的每個值進行相應的序列化;
4、如果提供了第三個函數,執行相應的格式化;
無論是考慮定義toJSON( ) 方法,還是使用函數過濾器,又或者是同時使用着兩種方法,理清順序都是非常重要的。
五、解析選項
JSON.parse( ) 方法也可以接收另一個參數,該參數是一個函數,將在每個鍵值對上調用——還原函數。
為了區別JSON.stringify()接收的替換(過渡)函數(replacer),這個函數被稱之為還原函數(reviver)
注意:如果還原函數 undefined ,則表示要從結果中刪除相應的鍵。如果返回其他值,則將該值插入到結果中
實例:
var person= { "name":"張三", "address":["中國 河北"], "age":20, "gender":"男" , "birth":1999, "releaseDate":new Date(2019, 9, 29) }; var jsonText = JSON.stringify(person); var jsonCopy = JSON.parse(jsonText, function(key, value){ if (key == "releaseDate") { return new Date(value); } else{ reture value; } }); console.log(jsonCopy.releaseDate.getFullYear());
在這個例子中,先是為person對象增加了一個releaseDate屬性,該屬性又保存了一個Date對象。
這個對象經過序列化之后變成了有效的JSON字符串,然后經過解析又在jsonCopy中還原為一個Date對象。
還原數據在遇到 "releaseDate" 鍵時,會基於相應的值創造一個新的Date對象。
結果就是jsonCopy.releaseDate 屬性中會保存一個Date 對象。正因為如此,才能基於這個對象調動 getFullYear( ) 方法。
