JSON和JavaScript對象互轉

JSON的本質是一個字符串,它之所以受歡迎,是因爲可以把JSON字符串解析爲有用的JavaScript對象。

ECMAScript5中,對解析JSON的行爲進行了規範,並定義了一個全局對象JSON,該對象有兩個方法:JSON.parse() 和 JSON.stringify()。

JSON.parse()

JSON.parse() 方法,用於把JSON字符串轉換爲原生的 JavaScript 值或對象。如:

  1. JSON.parse('1')      // 1
  2. JSON.parse('{}')      // {}
  3. JSON.parse('true')    // true
  4. JSON.parse('"foo"')   // "foo"
  5. JSON.parse('[1, 5, "false"]')   // [1, 5, "false"]
  6. JSON.parse('null')    // null
  7. var o = JSON.parse('{"name": "張三"}');
  8. o.name   // 張三

如果傳遞給JSON.parse()方法的字符串不是有效的JSON格式,JSON.parse()方法將報錯。如:

  1. JSON.parse("'String'")
  2. //Uncaught SyntaxError: Unexpected token u in JSON at position 0(…)
  3.  
  4. JSON.parse("undefined")
  5. //Uncaught SyntaxError: Unexpected token u in JSON at position 0(…)

除了直接進行字符串轉換之外,JSON.parse()方法也可以接收一個函數參數,該函數被稱爲還原函數。如果提供還原函數,則對象的每個成員都會調用這個函數。

還原函數接收兩個參數,一個鍵和一個值,並返回一個值。如果還原函數返回undefined,則表示要從結果中刪除相應的鍵;如果返回其他值,則將該值插入到結果中。如:

  1. var o = JSON.parse('{"a":1,"b":2}', function(key, value) {
  2.   if (key === ''){
  3.     return value;
  4.   }
  5.   if (key === 'a') {
  6.     return value + 10;
  7.   }
  8. });
  9. o.a // 11
  10. o.b // undefined

JSON 不能存儲 Date 對象。如果你需要存儲 Date 對象,需要將其轉換爲字符串,之後再將字符串轉換爲 Date 對象。在將日期字符串轉換爲Date對象時,經常要用到還原函數。如:

  1. var book = {
  2.   "title": "javascript",
  3.   "date": new Date(2018,1,21)
  4. }
  5. var jsonStr = JSON.stringify(book);
  6. console.log(jsonStr)
  7. //'{"title":"javascript","date":"2018-02-20T16:00:00.000Z"}'
  8.  
  9. var bookCopy = JSON.parse(jsonStr,function(key,value){
  10.   if(key == 'date'){
  11.     return new Date(value);
  12.   }
  13.   return value;
  14. })
  15. console.log(bookCopy.date.getFullYear());  //2018

說明:JSON.parse() 和 eval()

實際上,eval() 函數的功能類似於 JSON.parse()方法,也可以將json字符串轉換爲json對象。如:

  1. eval('(' + '{"a":1}'+')').a;  //1
  2. JSON.parse('{"a":1}').a;     //1

但是,eval() 函數可以執行不符合 JSON 格式的代碼。如:

  1. eval('(' + '{"a":alert(1)}'+')').a;  //彈出1
  2. JSON.parse('{"a":alert(1)}').a;     //報錯

在瀏覽器中運行上述代碼,eval() 函數就會彈出 alert 框,而 JSON.parse()方法將會報錯。由於 eval() 函數會爲惡意代碼提供可乘之機,因此應當儘量少使用 eval() 函數,而是使用更安全的 JSON.parse()方法。

JSON.stringify()

JSON.stringify()方法用於把JavaScript值(對象或者數組)序列化爲JSON字符串,並返回序列化後的JSON字符串。調用格式爲:

JSON.stringify(value[, replacer[, space]])

參數:

value: 必需。將要序列化爲JSON 字符串的值。

replacer: 可選。用於替換結果的函數、或僅包括指定的屬性數組。

space: 可選。用於文本添加縮進、空格和換行符。

序列化規則

序列化的本質,就是按照 JSON 的語法規則,把 JavaScript 值(對象或者數組),轉換爲 JSON 字符串,以方便存儲和數據交換。

1)數值、布爾值、字符串的包裝對象,會轉換成對應的原始值。如:

  1. JSON.stringify(1)      // "1"
  2. JSON.stringify(false)   // "false"
  3. JSON.stringify('abc')   // ""abc""
  4. JSON.stringify([1, "false", false])  // '[1,"false",false]'

2)鍵和值都被序列化,如果鍵名沒有引號,會自動爲它添加引號。如:

  1. JSON.stringify({x: 5, y: 6});     // "{"x":5,"y":6}"
  2. JSON.stringify({name: "張三"})    // '{"name":"張三"}'

3)數組,會轉換成數組格式。如:

  1. JSON.stringify([])   // "[]"
  2. JSON.stringify([1, "false", false])  // '[1,"false",false]'

4)對象,會轉換成對象格式。如:

  1. JSON.stringify({})   // "{}"
  2. JSON.stringify({x: 5, y: 6});     // "{"x":5,"y":6}"

5)正則表達式和數學對象,轉換成空對象的字符串形式。如:

  1. JSON.stringify(/foo/)   // "{}"
  2. JSON.stringify(Math)  // "{}"

6)日期對象和包裝對象,轉換成字符串。如:

  1. JSON.stringify(new Boolean(true))  //"true"
  2. JSON.stringify(new String('123'))   //""123""
  3. JSON.stringify(new Number(1))    //"1"
  4. JSON.stringify(new Date())        //""2016-09-20T02:26:38.294Z""

7) 如果非數組對象的成員是undefined、或任意的函數、或 symbol 值,這個成員會被省略。如果數組對象的成員是undefined、或任意的函數、或 symbol 值,則這些值被轉成null。如:

  1. JSON.stringify({
  2.   a: function(){},
  3.   b: undefined,
  4.   c: [ function(){}, undefined ]
  5. });
  6. // "{"c":[null,null]}"

8)不可枚舉的屬性,會被忽略。如:

  1. var obj = {};
  2. Object.defineProperties(obj, {
  3.   'foo': { value: 1,  enumerable: true },
  4.   'bar': { alue: 2, enumerable: false }
  5. });
  6. JSON.stringify(obj);  // "{"foo":1}"

9)所有以 symbol 爲鍵的屬性都會被完全忽略掉,即便 replacer 參數中強制指定包含了它們。如:

  1. JSON.stringify(
  2.     {[Symbol.for("foo")]: "foo"},
  3.     function (k, v) {
  4.         if (typeof k === "symbol"){
  5.             return "a symbol";
  6.         }
  7.     }
  8. );
  9. // undefined

replacer參數

replacer參數可以是一個函數或者一個數組。

1)函數

如果replacer參數是一個函數,則對象的每個成員都會調用這個函數。該函數接收兩個參數,一個是鍵(key),一個是值(value),執行序列化時,鍵和值都會被序列化。如:

  1. function dobule(key, value){
  2.   if (typeof value === "number") {
  3.     value = 2 * value;
  4.   }
  5.   return value;
  6. }
  7. console.log(JSON.stringify({a:1,b:2}, dobule()));  // "{"a":2,"b":4}"

如果replacer函數返回的是一個對象,則該對象被遞歸地序列化成JSON字符串,並且允許鍵爲空。如:

  1. var jsonObj = {a: {b: 1}};
  2. JSON.stringify(jsonObj, function(key, value) {
  3.   console.log("["+ key +"]:" + value);
  4.   return value;
  5. });

上述代碼中,對象 jsonObj 一共會被 function函數處理三次。第一次鍵名爲空,鍵值是整個對象 jsonObj;第二次鍵名爲 a,鍵值是 {b:1};第三次鍵名爲 b,鍵值爲 1。運行結果爲:

  1. []:[object Object]
  2. [a]:[object Object]
  3. [b]:1

如果replacer函數返回了undefined或沒有返回值,那麼相應的屬性會被忽略,這種情況將不會被序列化成JSON字符串。如:

  1. JSON.stringify({ a: "abc", b: 123 }, function (key, value) {
  2.   if (typeof(value) === "string") {
  3.     return undefined;
  4.   }
  5.   return value;
  6. })

上述代碼得到的結果爲:

  1. '{"b": 123}'

2)數組

如果replacer是一個數組,數組的值代表將被序列化成JSON字符串的屬性名。執行序列化時,則只對數組中所列舉的鍵執行序列化操作,就相當於實現一個過濾器的功能。如:

  1. var jsonObj = {
  2.   "title":"javascript",
  3.   "group":{
  4.     "a":1,
  5.     "b":2
  6.   }
  7. };
  8. console.log(JSON.stringify(jsonObj,["group","b"]))

上述代碼的運行結果爲:

  1. {"group":{"b":2}}

需要特別說明的是,過濾器功能只適用於對象,對數組無效。如,

  1. var jsonObj = [1,2];
  2. console.log(JSON.stringify(jsonObj,["0"]));   // "[1,2]"

space參數

默認情況下,JSON.stringify() 輸出的JSON字符串不包括任何空格字符或縮進。如:

  1. var jsonObj = {
  2.     "title":"javascript",
  3.     "group":{
  4.         "name":"jia",
  5.         "tel":12345
  6.     }
  7. };
  8. JSON.stringify(jsonObj);
  9. //{"title":"javascript","group":{"name":"jia","tel":12345}}

通過指定space參數,可以在JSON字符串中添加空格或字符串,用於增加返回的JSON字符串的可讀性。space參數的值可以是數值或字符串。

1)數值

如果space參數的值是數值,則序列化的字符串中,每一級別會比上一級別縮進指定數目的空格(不超過10個空格)。如:

  1. console.log(JSON.stringify({ p1: 1, p2: 2 }, null, 2));

上述代碼得到的結果爲:

  1. {
  2.   "p1": 1,
  3.   "p2": 2
  4. }

2)字符串

如果space參數的值是字符串(字符串不超過10個字符),則序列化的字符串中,每一級別會比上一級別的縮進,使用該字符串填充。如:

  1. console.log(JSON.stringify({ p1:1, p2:2 }, null, '|-'));

上述代碼得到的結果爲:

  1. {
  2. |-"p1": 1,
  3. |-"p2": 2
  4. }

注意:

在序列化一個對象時,如果該對象擁有 toJSON()方法,那麼該 toJSON() 方法就會覆蓋該對象默認的序列化行爲: 被序列化的不是那個原始對象,而是調用 toJSON() 方法後返回的那個對象。如:

  1. var obj = {
  2.   foo: 'foo',
  3.   toJSON: function () {
  4.     return 'bar';
  5.   }
  6. };
  7. JSON.stringify(obj);       // '"bar"'
  8. JSON.stringify({x: obj});   // '{"x":"bar"}'

如果 toJSON()方法返回 undefined,此時如果包含它的對象嵌入在另一個對象中,會導致該對象的值變成 null。而如果包含它的對象是頂級對象,結果就是 undefined。

Date 對象部署了一個自己的 toJSON()方法,它會自動將 Date 對象轉換成日期字符串。如:

  1. JSON.stringify(new Date("2017-12-29"))
  2. // "2017-12-29T00:00:00.000Z"

關於作者

歪脖先生,十五年以上軟件開發經驗,酷愛Web開發,精通 HTML、CSS、JavaScript、jQuery、JSON、Python、Less、Bootstrap等,著有《HTML寶典》、《揭祕CSS》、《Less簡明教程》、《JSON教程》、《Bootstrap2用戶指南》、《Bootstrap3實用教程》,並全部在 GitHub 上開源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章