JSON總結

0. 背景

XML曾是web結構化數據傳輸的事實標準,然而業界一直不乏質疑XML的聲音,也發展出了一些替代方案,JSON就是最流行的一種。

JSON(JavaScript Object Notation,JavaScript對象表示法)是JavaScript語言的嚴格子集,通過JavaScript的模式來結構化表示數據。JSON擁有類似XML的特點:純文本、自我描述性、層級結構等。並不是只有JavaScript可以使用JSON,作爲一種廣泛使用的數據格式,絕大多數語言已經支持JSON的解析和序列化。

1. 基本語法

JSON可以表示三種類型的值:
  • 基本類型:數值、布爾值、字符串、null
  • 對象:一組有序鍵值對。每個鍵值對的值又可以是任意類型
  • 數組:一組有序值的列表,可按數值索引訪問。每個值都可以是任意類型
JSON的數據表示雖然都是JavaScript的語法形式,但並不支持函數、變量等結構,也不支持undefined類型,畢竟JSON只是一種數據的結構化表示方法。

1.1 基本類型

數值類型包括整型和浮點型,與JavaScript語法相同,如:
128    3.14
字符串用雙引號括起來(注意,JavaScript中字符串可用雙引號或單引號表示,而JSON只能用雙引號),如:
"Rick and Morty"
布爾值和null都和JavaScript表示相同。

1.2 對象

JSON與JavaScript字面量對象稍有不同:JSON中屬性必須加雙引號
{
  "name": "Morty",
  "age": 14
}
當然不必聲明某個對象變量,畢竟JSON只是存儲數據。
屬性值可以是複雜類型,例如實現對象嵌套:
{
  "name": "Morty",
  "age": 14,
  "grandpa": {
    "name": "Rick",
    "age": 70
  }
}
再次提醒,JSON對象屬性必須加雙引號,字符串也必須加雙引號。

1.3 數組

JSON數組就是JavaScript中數組的字面量形式,只是去掉了變量聲明。
將三種類型:基本類型、對象和數組結合起來構成更復雜的數據結合:
{
  "name": "Morty",
  "age": 14,
  "grandpa": {
    "name": "Rick",
    "age": 70
  },
  "parent": [
    {"name": "Jerry", "age": 35},
    {"name": "Beth", "age": 35}    
  ]
}

2. 解析和序列化

JSON被廣泛用於web交換數據並不僅由於其類似JavaScript的語法,更重要的原因是JSON數據可以直接解析成JavaScript對象,比XML方便了很多。

2.1 JSON對象

早期JSON的解析器大多使用JavaScript的eval()函數,但由於eval()會執行JavaScript代碼而存在一些安全問題。ECMAScript 5爲JSON解析進行了標準化,定義了全局對象JSON。利用JSON對象將JSON轉化爲JavaScript對象更爲通用的做法。(瀏覽器支持:IE8+,Firefox3.5+,safari4+等)
JSON對象有兩個方法:
  • stringify(),將JavaScript對象序列化爲JSON字符串
  • parse(),和將JSON字符串解析爲JavaScript對象。
例如:
var computer = {
  CPU: "i7 8700K",
  GPU: "GTX 1080Ti",
  RAM: "16G DDR4",
  DISK: ["256G SSD", "2T HDD"],
};

var jsonTest = JSON.stringify(computer); //序列化

var computerCopy = JSON.parse(jsonTest); //解析
默認情況下,stringify()輸出的JSON字符串中不會包含任何空白字符,上例中jsonTest如下:
{"CPU":"i7 8700K","GPU":"GTX 1080Ti","RAM":"16G DDR4","DISK":["256G SSD","2T HDD"]}
而parse()函數會忽略JavaScript對象中的函數、原型成員,此外undefined類型屬性也會被跳過。

2.2 序列化

stringify()參數除了待序列化的JavaScript對象外,還可以接受另外兩個參數:
  • 第一個參數:過濾器,一個數組或函數
  • 第二個參數:選項,選擇JSON字符串是否縮進
下面繼續以computer對象舉例說明。

2.2.1 過濾器

首先看過濾器的用法:
1)傳入數組
例如:
var jsonTest = JSON.stringify(computer, ["CPU", "GPU"]);
jsonTest如下:
{"CPU":"i7 8700K","GPU":"GTX 1080Ti"}
即只序列化這個數組中包含的屬性。
2)傳入函數
稱爲過濾函數或替換函數,該函數會接受兩個參數:屬性名和屬性值,即一個鍵值對。例如:
var jsonTest = JSON.stringify(computer, function(key, value) {
  switch(key) {
    case "CPU":
      return undefined;
    case "RAM":
      return 16;
    case "DISK":
      return value.join("500G SSHD");
    default:
      return value;
  }
});

var computerCopy = JSON.parse(jsonTest);
此時jsonTest如下:
{"GPU":"GTX 1080Ti","RAM":16,"DISK":"256G SSD500G SSHD2T HDD"}
傳入函數的行爲:
  • 根據屬性名找到要處理的對象屬性
  • 函數返回值就是處理後的屬性值(若返回undefined則該屬性會略過)
上例中屬性"CPU"被略過,屬性"RAM"被修改爲16,屬性"DISK"被添加一個元素,其餘屬性原樣返回。

2.2.2 縮進控制

stringify()第三個參數用於控制輸出中的縮進和空白符。可以傳入一個數字或字符串。
1)傳入數字
例如傳入數字4:
var jsonTest = JSON.stringify(computer, null, 4);
此時jsonTest如下:
{
  "CPU": "i7 8700K",
  "GPU": "GTX 1080Ti",
  "RAM": "16G DDR4",
  "DISK": [
      "256G SSD",
      "2T HDD"
  ]
}

可見縮進被設置爲傳入數值4。
這種方式可以提高JSON字符串的可讀性,允許傳入的最大數字爲10,大於10的數值都會按10處理。
2)傳入字符串
例如傳入字符串"--":
{
--"CPU": "i7 8700K",
--"GPU": "GTX 1080Ti",
--"RAM": "16G DDR4",
--"DISK": [
----"256G SSD",
----"2T HDD"
--]
}
同樣縮進字符串長度最大爲10,超過10則只使用前10個字符。

2.2.3 toJSON()

JSON.stringify()方法還是不能完美滿足對於某些對象進行自定義序列化的需求,我們可以調用對象自身的toJSON()方法返回其自定義JSON格式,達到類似於toString()的靈活性要求。
例如爲computer對象添加toJSON()方法:
var computer = {
  CPU: "i7 8700K",
  GPU: "GTX 1080Ti",
  RAM: "16G DDR4",
  DISK: ["256G SSD", "2T HDD"],
  toJSON: function() {
    return this.CPU + ", " + this.GPU;
  }
};

var jsonTest = JSON.stringify(computer);
此時jsonText爲:
"i7 8700K, GTX 1080Ti"
對象的toJSON()方法,可以返回任何自定義值。toJSON()也是完成過濾器的功能,是過濾函數的一種補充。

因此stringify()方法的序列化順序對最終結果十分重要。將某個對象傳入stringify()後序列化該對象的順序爲:
1)若該對象有toJSON()方法則調用該方法;否則按默認順序執行
2)若提供了第二個參數則按照參數進行過濾
3)若提供了第三個參數則按照參數進行格式化

2.3 解析

JSON.parse()也可以接受另一個參數:一個函數,也是作用於每個鍵值對。爲了區分JSON.stringify()上的替換函數(replacer),這個函數被稱爲還原函數(reviver),實際上兩個函數的參數是相同的:都是一對鍵值對。
例如:
var computer = {
  CPU: "i7 8700K",
  GPU: "GTX 1080Ti",
  RAM: "16G DDR4",
  DISK: ["256G SSD", "2T HDD"],
  releaseDate: new Date(2017, 11, 11)
};

var jsonTest = JSON.stringify(computer);

var computerCopy = JSON.parse(jsonTest, function(key, value) {
  if (key === "releaseDate") {
    return new Date(value);
  } else {
    return value;
  }
});

console.log(computerCopy.releaseDate.getFullYear());
上例computer對象的屬性releaseDate中保存着一個Date對象。序列化後的jsonTest中保存着一個Date值,若不在parse()中將其轉化爲一個Date對象,就無法調用getFullYear()方法。

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