一、序列化与反序列化的概念
序列化(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( ) 方法。