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()方法。

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