5分鐘瞭解JSON那些事兒

JSON

簡介

  • JSON是JavaScript Object Notation(JavaScript對象表示法)的縮寫
  • JSON是一種數據格式, 而不是一種編程語言, 用來表示結構化數據
  • JSON是JavaScript的一個嚴格子集
  • JSON並不從屬於JavaScript, 不少編程語言均可以用JSON數據格式

語法

JSON語法能夠表示如下三種類型的值:javascript

  • 簡單值: 字符串/數值/布爾值/null, 可是不支持undefined
  • 對象
  • 數組

JSON不支持變量/函數/對象實例java

簡單值

字符串編程

"Hello JSON"

數字json

66

布爾值數組

true

null數據結構

null

對象

和JavaScript對比來看 ->編程語言

  • JavaScript表示對象

鍵值對的鍵能夠加引號也能夠不加, 若是加引號, 能夠加單引號也能夠加雙引號函數

// 最多見就這麼寫
const obj1 = {
  foo: 'bar',
  num: 66,
  status: true
};
// 這麼寫也oconstet obj2 = {
  'foo': 'bar',
  'num': 66,
  'status': true
};
// 這麼寫也ok
const obj1 = {
  "foo": "bar",
  "num": 66,
  "status": true
};
  • JSON表示對象

鍵值對的鍵必須加雙引號(手寫JSON時必定要注意)this

對象沒有變量聲明, 由於JSON根本就沒有變量的概念(它不是一個編程語言)code

末尾沒有分號

{
  "foo": "bar",
  "num": 66,
  "status": true
}

和JavaScript類似, 對象能夠嵌套對象

{
  "foo": "bar",
  "num": 66,
  "status": true,
  "baz": {
    "num": 88
  }
}

注, 同名屬性能夠在不一樣的對象中, 可是不能出如今同一個對象中

數組

和JavaScript對比來看 ->

  • JavaScript表示數組
let arr = ['hello', 66, true];
  • JSON表示數組

一樣沒有變量聲明和結尾的分號, 同時注意字符串簡單值要加雙引號

["hello", 66, true]

數組和對象結合起來能夠構成複雜的集合, 好比students.json文件中多是這樣嬸兒的

[
  {
    "name": "小明",
    "age": 10,
    "score": {
      "math": 88,
      "english": 99
    }
  },
  {
    "name": "小強",
    "age": 11,
    "score": {
      "math": 98,
      "english": 96
    }
  }
]

看到以上同JavaScript的不一樣之處, 咱們能夠知道爲何說JSON是JavaScript的一個嚴格子集了吧

JSON序列化與解析

基本用法

ECMAScript5定義了全局對象JSON, 用來解析JSON字符串

簡單來講, JSON對象有兩個方法

  • JSON.stringify(): 把JavaScript對象序列化爲JSON字符串
  • JSON.parse(): 把JSON字符串解析爲原生JavaScript值
const book = {
  name: 'Learn JavaScript in One Day',
  pages: 1
};

const jsonText = JSON.stringify(book); // 序列化
// "{"name":"Learn JavaScript in One Day","pages":1}"

const parseText = JSON.parse(jsonText); // 解析
// {name: "Learn JavaScript in One Day", pages: 1}

默認狀況下, JSON.stringify()輸出的JSON字符串不包含任何空格字符或縮進(是否是給咱們提供了一種將去除數據中無用的空白和縮進的方法呢).

在序列化JavaScript對象時, 全部函數及原型成員都會被有意忽略, 不體如今結果中. 此外, 值爲undefined的任何屬性也都會被跳過. 結果中最終都是值爲有效JSON數據類型的實例屬性.

const book = {
  name: 'Learn JavaScript in One Day',
  pages: 1,
  foo: undefined
};

const jsonText = JSON.stringify(book); // 序列化
// "{"name":"Learn JavaScript in One Day","pages":1}"
// foo的值爲undefined, 所被忽略掉了

將JSON字符串直接傳遞給JSON.parse()就能夠獲得相應的JavaScript值.

JSON.parse()就是JSON.stringify()的逆向操做. 將一個JavaScript值序列化以後再解析JSON.parse(JSON.stringify(foo))和原來的foo幾乎同樣.

注意, 爲何說是幾乎同樣呢?

  1. 若是foo是一個對象或數組, 那麼這麼一折騰就變成了兩個不一樣的對象或數組了;

    這就提供了一種克隆對象和數組的方法😆

  2. 若是foo是一個對象, 而且foo中有值爲undefined的屬性, 那麼在序列化的過程當中它會被篩掉;
  3. 若是foo是一個數組, 而且其中有undefined的項, 那麼在序列化過程當中undefined的項會變成null;
  4. 若是foo是一個簡單值(數字/布爾值/字符串), 那麼這麼一折騰結果徹底是相等的.
const foo1 = { a: 1 };
JSON.parse(JSON.stringify(foo1)) === foo1; // false
const foo2 = [1, 2];
JSON.parse(JSON.stringify(foo2)) === foo2; // false
const foo3 = { a: 1, b: undefined };
JSON.parse(JSON.stringify(foo3)); // {a: 1}
const foo4 = [1, true, undefined];
JSON.parse(JSON.stringify(foo4)); // [1, true, null]
const foo5 = true;
JSON.parse(JSON.stringify(foo5)) === foo5; // true

若是傳給JSON.parse()的字符串不是有效的JSON, 該方法會拋出錯誤.

進階用法

序列化選項

JSON.stringify()除了要序列化的JavaScript對象外, 還能夠接收另外兩個參數, 這兩個參數用於指定以不一樣的方式序列化JavaScript對象.

第一個參數是個過濾器, 能夠是一個數組, 也能夠是一個函數;

第二個參數是一個選項, 表示是否在JSON字符串中保留縮進.

1.過濾結果

若是過濾器參數是數組, 那麼JSON.stringify()的結果中將只包含數組中列出的屬性

const book = {
  name: 'Learn JavaScript in One Day',
  pages: 1
};
JSON.stringify(book, ['name']);
// "{"name":"Learn JavaScript in One Day"}"

再看下面的例子🌰:

const stu = {
  name: "小明",
  age: 10,
  score: {
    math: 88,
    english: 99
  }
};
JSON.stringify(stu, ['name', 'score']);
// "{"name":"小明","score":{}}"

注意, 最後結果中score是一個空對象, 那麼小明的成績哪兒去了?

好像咱們的目的是要顯示name和score屬性, 只去掉age就好了, 可是score對象中咋啥也沒有了?

那咱們把stu對象從新改改試試:

var stu = {
  name: "小明",
  age: 10,
  score: {
    math: 88,
    english: 99,
    name: 'xiuxiu~',
    score: 100
  }
};
JSON.stringify(stu, ['name', 'score']);
// "{"name":"小明","score":{"name":"xiuxiu~","score":100}}"

這下看明白了麼, 原來這個過濾是對每一層級的對象都過濾一遍.

若是第二個參數是函數, 行爲會稍有不一樣. 傳入的函數接收兩個參數, 屬性名(鍵)和屬性值(值). 根據屬性名能夠知道應該如何處理要序列化的對象中的屬性. 屬性名只能是字符串, 而在值並不是鍵值對兒結構的值時, 鍵名能夠是空字符串. 函數返回的值就是相應鍵的值, 若是函數返回了undefined, 那麼相應的屬性會被忽略.

const stu = {
  name: "小明",
  age: 10,
  score: {
    math: 88,
    english: 99
  }
};
const newStu = JSON.stringify(stu, (key, value) => {
  switch(key) {
    case 'math':
      return 100;
    case 'english':
      return 100;
    default:
      return value;
  }
});
// "{"name":"小明","age":10,"score":{"math":100,"english":100}}"
// 成功將小明的成績改爲了100分, 哈哈哈~

2.字符串縮進

JSON.stringify()方法的第三個參數用於控制結果中的縮進和空白符. 若是這個參數是一個數值, 那它表示的是每一個級別縮進的空格數.

JSON.stringify()會在結果字符串中插入換行符以提升可讀性.

最大縮進空格數爲10, 全部大於10的值都會自動轉換爲10.

const stu = {
  name: "小明",
  age: 10,
  score: {
    math: 88,
    english: 99
  }
};
JSON.stringify(stu, null, 4); // 4個空格的縮進
/* 序列化後的結果
"{
    "name": "小明",
    "age": 10,
    "score": {
        "math": 88,
        "english": 99
    }
}"
*/

若是縮進參數是一個字符串而非數值, 則這個字符串將在JSON字符串中被用做縮進字符(再也不使用空格).

縮進字符串最長不能超過10個字符長. 若是字符串長度超過了10個, 結果中將只出現前10個字符.

JSON.stringify(stu, null, '--');
/* 序列化後的結果
"{
--"name": "小明",
--"age": 10,
--"score": {
----"math": 88,
----"english": 99
--}
}"
*/

3.toJSON

有時候, JSON.stringify()仍是不能知足對某些對象進行自定義序列化的需求. 在這些狀況下, 能夠給對象定義toJSON()方法, 返回其自身的JSON數據格式. 原生Date對象有一個toJSON()方法,可以將JavaScript的Date對象自動轉換成ISO8601日期字符串(與在Date對象上調用toISOString()的結果徹底同樣).

JSON.stringify(new Date());
// 序列化後的結果: ""2019-04-08T11:31:05.778Z""
new Date().toJSON();
new Date().toISOString();
// 直接調用toJSON和toISOString方法一樣能獲得字符串: "2019-04-08T11:31:44.432Z"

可讓toJSON()方法返回任何值, 它都能正常工做.

const stu = {
  name: "小明",
  age: 10,
  score: {
    math: 88,
    english: 99
  },
  toJSON() {
        return this.name;
  }
};
JSON.stringify(stu);
// 序列化後的結果: ""小明""

toJSON()能夠做爲函數過濾器的補充, 所以理解序列化的內部順序十分重要. 假設把一個對象傳入JSON.stringify(), 序列化該對象的順序以下:

(1) 若是存在toJSON()方法並且能經過它取得有效的值, 則調用該方法. 不然, 返回對象自己;

(2) 若是提供了第二個參數, 應用這個函數過濾器. 傳入函數過濾器的值是第(1)步返回的值;

(3) 對第(2)步返回的每一個值進行相應的序列化;

(4) 若是提供了第三個參數, 執行相應的格式化.

解析選項

JSON.parse()方法也能夠接收另外一個參數, 該參數是一個函數, 將在每一個鍵值對兒上調用. 爲了區別 JSON.stringify()接收的替換(過濾)函數(replacer), 這個函數被稱爲還原函數(reviver), 但實際上這兩個函數的簽名是相同的——它們都接收兩個參數, 一個鍵和一個值, 並且都須要返回一個值.

若是還原函數返回undefined, 則表示要從結果中刪除相應的鍵; 若是返回其餘值, 則將該值插入到結果中.

const stu = {
  name: "小明",
  age: 10,
  score: {
    math: 88,
    english: 99
  }
};
const jsonText = JSON.stringify(stu);
JSON.parse(jsonText, (key, value) => {
  if (key === 'name') {
    return value + '牛逼了!';
  } else {
    return value;
  }
});
/* 解析結果
{
  age: 10
  name: "小明牛逼了!"
  score: { math: 88, english: 99 }
}
*/

小結

JSON是一個輕量級的數據格式, 能夠簡化表示複雜數據結構的工做量. JSON使用JavaScript語法的子集表示對象、數組、字符串、數值、布爾值和null.

ECMAScript5定義了一個原生的JSON對象, 能夠用來將對象序列化爲JSON字符串或者將JSON數據解析爲JavaScript對象. JSON.stringify()JSON.parse()方法分別用來實現上述兩項功能. 這兩個方法都有一些選項, 經過它們能夠改變過濾的方式, 或者改變序列化的過程.

相關文章
相關標籤/搜索