JSON序列化和反序列化1、JSON序列化1.1、JSON.stringify()函数2.2、自定义toJSON()函数2、JSON反序列化2.1、JSON.parse()函数2.2、eval()函数1、JSON序列化JSON序列化即将JSON对象处理为JSON字符串的过程以方便数据的传输。JSON序列化可以通过两种方式来实现一种是调用JSON对象内置的stringify()函数一种是为对象自定义toJSON()函数。1.1、JSON.stringify()函数JSON.stringify()函数是将一个JavaScript对象或者数组转换为JSON字符串它的基本用法如下所示。JSON.stringify(value[,replacer[,space]])value参数表示待处理成JSON字符串的JavaScript值通常为对象或者数组。replacer参数是一个可选参数。如果其值为一个函数则表示在序列化过程中被序列化值的每个属性都会经过该函数的处理如果其值为一个数组则表示只有包含在这个数组中的属性名才会被序列化到最终的JSON字符串中如果该值为null或者未传递则value参数对应值的所有属性都会被序列化。space是一个可选参数用于指定缩进用的空白字符串美化输出。如果参数是个数字则代表有多少个空格上限值为10如果该参数的值小于1则意味着没有空格如果参数为字符串则取字符串的前十个字符作为空格如果没有传入参数或者传入的值为null将没有空格。通过以下代码来看看JSON. stringify()函数的基本使用方法。首先定义一个待序列化的对象。varobj{name:kingx,age:15,address:String(北京市),interest:[basketball,football],email:zhouxiongking163.com};console.log(JSON.stringify(obj));当只传递第一个参数时输出的结果如下所示:{name:kingx,age:15,address: 北京市,interest:[basketball,football],email:zhouxiongking163.com}当传递了replacer参数并且值为一个函数时函数所做的处理是假如属性值为字符串类型则将值转换为大写。functionreplacerFn(key,value){if(typeofvaluestring){returnvalue.toUpperCase();}returnvalue;}console.log(JSON.stringify(obj,replacerFn));输出的结果如下所示{name:KINGX,age:15,address:北京市,interest:[BASKETBALL,FOOTBALL],email:ZHOUXIONGKING163.COM}通过结果可以看出name、address、email属性值为字符串类型其值都转换成了大写字母但是interest属性值为数组类型为什么数组中的值也转换成了大写字母呢这就涉及递归调用的问题在JSON序列化时如果属性值为对象或者数组则会继续序列化该属性值直到属性值为基本类型、函数或者Symbol类型才结束。针对上面的实例obj对象的name、address、email属性值经过replacerFn()函数处理后会返回大写的值age属性值为数字类型不做任何处理会直接返回值本身而interest属性值类型为数组return回来后数组中的每个值会再次经过replacerFn()函数处理因为数组中的元素此时都为string类型返回的值会转换成大写。当replacer参数为一个数组时数组元素的值代表将要进行序列化成JSON字符串的属性名。如上面的例子我们调用以下函数并且只序列化name属性和age属性的值。console.log(JSON.stringify(obj,[name,age]));输出的结果如下所示。{name:kingx,age:15}关于JSON序列化有以下一些注意事项。非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。布尔值、字符串、数字的包装对象在序列化过程中会被转换为对应的原始值。以下是一段序列化数组元素为多种包装类型的的代码。JSON.stringify([newNumber(1),newString(false),newBoolean(false)]);输出的结果如下所示:[1,false,false]在非数组对象中undefined、任意的函数及Symbol值在序列化时会被忽略在数组对象中它们会被序列化为null。JSON.stringify({x:undefined,y:Object,z:Symbol()});在非数组对象中输出的结果如下所示。{}JSON.stringify([undefined,Object,Symbol()]);在数组对象中输出的结果如下所示。[null,null,null]对包含循环引用对象进行序列化时会抛出异常。定义两个循环引用的对象并调用stringify()函数输出结果。vara{name:z z z};varb{name:vvv};a.childb;b.parenta;console.log(JSON.stringify(a));运行后控制台会抛出异常。提示信息为循环引用结果转换为JSON失败。TypeError:Converting circular structure toJSON所有以symbol为属性键的属性都会被完全忽略掉。JSON.stringify({[Symbol(foo)]:foo});输出的结果如下所示。{}不可枚举的属性值会被忽略。创建一个对象包含一个可枚举的属性、一个不可枚举的属性对其进行序列化。varpObject.create(null,{name:{value:xiaoming,enumerable:false},age:{value:15,enumerable:true}});console.log(JSON.stringify(p));输出的结果如下所示。{age:15}2.2、自定义toJSON()函数如果一个被序列化的对象拥有toJSON()函数那么toJSON()函数就会覆盖默认的序列化行为被序列化的值将不再是原来的属性值而是toJSON()函数的返回值。toJSON()函数用于更精确的控制序列化可以看作是对stringify()函数的补充。我们同样使用前面例子定义一个对象增加toJSON()函数。varobj2{name:kingx,age:15,address:String(北京市),interest:[basketball,football],email:zhouxiongking163.com,toJSON:function(){// 只返回name和age属性值并且修改keyreturn{Name:this.name,Age:this.age};}};调用JSON.stringify()函数。console.log(JSON.stringify(obj2));console.log(JSON.stringify({name:obj2},[name]));输出的结果如下所示。{Name:kingx,Age:15}{name:{}}对于第一个结果因为obj2有toJSON()函数所以返回值为带有Name和Age属性的值“{“Name”:“kingx”,“Age”:15}”​然后直接进行序列化得到结果。对于第二个结果obj2对象在调用toJSON()函数后的返回值是“{“Name”:“kingx”,“Age”:15}”​实际进行序列化的值为“{name:{“Name”:“kingx”,“Age”:15}}”​。此时传递了replacer参数因为replacer为一个数组过滤的是name属性但是name属性值为一个对象则需要对对象中的每个属性递归序列化而“Name”和“Age”与要过滤的属性“name”值不相等所以过滤后的值就为一个空对象{}所以最终结果为“{“name”:{}}”​。因此序列化处理的顺序如下。如果待序列化的对象存在toJSON()函数则优先调用toJSON()函数以toJSON()函数的返回值作为待序列化的值否则返回JSON对象本身。如果stringify()函数提供了第二个参数replacer则对上一步的返回值经过replacer参数处理。如果stringify()函数提供了第三个参数则对JSON字符串进行格式化处理返回最终的结果。2、JSON反序列化JSON反序列化即将JSON字符串转换为JSON对象的过程得到的结果用于在JavaScript中做逻辑处理。JSON反序列化的实现方式有两种一种是使用JSON对象内置的parse()函数一种是使用eval()函数。2.1、JSON.parse()函数JSON.parse()函数用来解析JSON字符串构造由字符串描述的JavaScript值或对象它的语法如下所示。JSON.parse(text[,reviver])text表示待解析的JSON字符串。reviver是一个可选参数。如果是一个函数则规定了原始值在返回之前如何被解析改造。如果被解析的JSON字符串是非法的则会抛出异常。我们首先来看看JSON.parse()函数的基本使用方法包括对数组、对象、基本数据类型的处理。JSON.parse([1,2,3,true]);// Array [1, 2, 3, true]JSON.parse({name:小明,age:14});// Object {name: 小明, age: 14}JSON.parse(true);// trueJSON.parse(123.45);// 123.45JSON.parse()函数还可以接收一个函数用来处理JSON字符串中的每个属性值。当属性值为一个数组或者对象时数组中的每个元素或者对象的每个属性都会经过reviver参数对应的函数处理。执行的顺序是从最内层开始按照层级顺序依次向外遍历。我们通过以下这段代码看看parse()函数传递reviver参数的处理情况。首先定义JSON字符串然后调用JSON.parse()函数进行解析。varjsonStr{name:kingx,age:15,address:北京市,interest:[basketball,football],children:[{name:kingx2,age:20}],email:zhouxgking163.com};varresultJSON.parse(jsonStr,function(key,value){if(keyname){returnvalue同学;}if(keyage){returnvalue*2;}returnvalue;});console.log(result);{name:kingx同学,age:30,address:北京市,interest:[basketball,football],children:[{name:kingx2同学,age:40}],email:zhouxiongking163.com}通过结果可以看出解析后的name属性的值都加上了“同学”​age属性的值都乘以了2。当使用JSON.parse()函数解析JSON字符串时需要注意两点。JSON字符串中的属性名必须用双引号括起来否则会解析错误。以下是一些正确和错误的写法。varjson{name:kingx};// 这个是正确的JSON格式varjson{\name\:\kingx\};// 这个也是正确的JSON格式varjson{name:kingx};// 这个是错误的JSON格式因为属性名没有用双引号括起来varjson{name:kingx};//这个也是错误的JSON格式属性名应该用双引号括起来而它用了单引号JSON字符串不能以逗号结尾否则会解析异常。JSON.parse([1, 2, 3, 4, ]);// 解析异常数组最后一个元素后面出现逗号JSON.parse({foo : 1, });// 解析异常最后一个属性值后面出现逗号2.2、eval()函数eval()函数用于计算JavaScript字符串并把它作为脚本来执行。在使用eval()函数进行JSON反序列化时其语法如下所示。eval((str))其中str表示待处理的字符串。这里为什么要使用括号将拼接出来的字符串括起来呢因为JSON字符串是以“{}”开始和结束的在JavaScript中它会被当作一个语句块来处理所以必须强制将它处理成一个表达式所以采用括号。varjson1{name:kingx};varjson2{address:[beijing,shanghai]};console.log(eval((json1)));// {name: kingx}console.log(eval((json2)));// {address: [beijing, shanghai]}