**解析与序列化** JSON 之所以流行,拥有与 JavaScript 类似的语法并不是全部原因,更重要的一个原因是,可以很容易把JSON 数据结构解析为有用的 JavaScript 对象 bookThreeTitle=books[2].title 下面是在 DOM结构中查找数据的代码: bookThreeTitle=loadXMLDoc("books.xml").getElementsByTagName("book")[2].getAttribute("title") 看看这些多余的方法调用,就不难理解为什么 JSON 能得到 JavaScript 开发人员的热烈欢迎了。从s此以后,JSON 就成了 Web 服务开发中交换数据的事实标准。 - JSON对象 - 序列化选项 - 解析选项 # JSON对象 JSON 对象有两个方法: stringify() 和 parse() 。在最简单的情况下,这两个方法分别用于把JavaScript 对象序列化为 JSON 字符串和把 JSON 字符串解析为原生 JavaScript 值。 也可以使用JavaScript 的 eval() 函数来解析JSON字符串,但是使用 eval() 对 JSON 数据结构求值存在风险,因为可能会执行一些恶意代码。还有就是比较好性能;(eval可以解析任何字符串,属于比较底层的方法,虽然通杀但是一般不会使用;) **JSON.stringify的用法**,JSON对象转为JSON字符串 var JSONObj = { title: "Professional JavaScript", authors: [ "Nicholas C. Zakas" ], edition: 3, year: 2011, testUndefined:undefined, testFunction:function(){return 2+2} }; var JSONStr = JSON.stringify(JSONObj); console.log(JSONStr);//{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011} console.log(typeof JSONObj);//object console.log(typeof JSONStr);//string 默认情况下, JSON.stringify() 输出的 JSON 字符串不包含任何空格字符或缩进, 在序列化 JavaScript 对象时,所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined 的任何属性也都会被跳过。结果中最终都是值为有效 JSON 数据类型的实例属性。 上面的 **testUndefined*** 和 **testFunction** 将不会出现最终的结果中; **JSON.parse的用法**,JSON字符串转为JSON对象; var JSONStr1 = '{title: "Professional JavaScript",authors: ["Nicholas C. Zakas"],edition: 3,year: 2011}'; var JSONStr2 = '{"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],"edition": 3,year: 2011}'; var JSONStr3 = '{"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],"edition": 3,"year": 2011}'; var JSONStr4 = '{"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],"edition": 3,"year": 2011,"testUndefined":undefined}'; //var JSONObj1 = JSON.parse(JSONStr1); // 报错啦,属性值没有"" //var JSONObj2 = JSON.parse(JSONStr2); // 报错啦,属性值没有全部用"" var JSONObj3 = JSON.parse(JSONStr3); // 成功 //var JSONObj4 = JSON.parse(JSONStr4); // 报错啦,不能含有undefined,function等数据 console.log(JSONObj3);//{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011} console.log(typeof JSONObj3);//object > 如果传给 JSON.parse() 的字符串不是有效的 JSON,该方法会抛出错误。 # 序列化选项 ( JSON.stringify()的深入理解 ) 实际上, JSON.stringify() 除了要序列化的 JavaScript 对象外,还可以接收另外两个参数,这两个参数用于指定以不同的方式序列化 JavaScript 对象。第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在 JSON 字符串中保留缩进。单独或组合使用这两个参数,可以更全面深入地控制 JSON 的序列化 JSON.stringify(jsonObj,**arguments1**,**arguments2**) - arguments1 过滤器的作用,用来过滤结果; - arguments2 字符串缩进的作用; ##### 过滤器(用来过滤结果) - **数组过滤** var book={ "title":"book title", authors:[ "one", "two", "three" ], edition:2, year:2016 }; var JSONStr=JSON.stringify(book,["title","year","other"]); console.log(JSONStr);//{"title":"book title","year":2016} console.log(typeof JSONStr);//string JSON.stringify() 的第二个参数是一个数组,其中包含三个字符串: "title"、"year"、"other"。这两个属性与将要序列化的对象中的属性是对应的,因此在返回的结果字符串中,就只会包含这两个属性;因为源数据中没有other这个键值对;匹配不到,就会忽略掉; - **函数过滤** var book={ "title":"book title", authors:[ "one", "two", "three" ], edition:2, year:2016 }; var JSONStr=JSON.stringify(book,function(key,value){ switch (key){ case "authors": return value.join("+++++");//如果键为 "authors" ,就将数组连接为一个字符串; case "year": return 5000+"change";//如果键为 "year" ,则将其值设置为 5000change ; case "edition": return undefined;//如果键为 "edition" ,通过返回 undefined 删除该属性 default : return value;//最后,一定要提供 default 项,此时返回传入的值,以便其他值都能正常出现在结果中。 } }); console.log(JSONStr);//{"title":"book title","authors":"one+++++two+++++three","year":"5000change"} console.log(typeof JSONStr);//string 这里,函数过滤器根据传入的键来决定结果 Firefox 3.5 和 3.6 对 JSON.stringify() 的实现有一个 bug,在将函数作为该方法的第二个参数时这个 bug 就会出现,即这个函数只能作为过滤器:返回 undefined 意味着要跳过某个属性,而返回其他任何值都会在结果中包含相应的属性。Firefox 4 修复了这个 bug。(未亲自验证) ##### 字符串缩进 var book={ "title":"book title", authors:[ "one", "two", "three" ], edition:2, year:2016 }; var JSONStr=JSON.stringify(book,null,4); console.log(JSONStr); console.log(typeof JSONStr);//string 输出的结果如下: { "title": "book title", "authors": [ "one", "two", "three" ], "edition": 2, "year": 2016 } **也可以用字符替换空格**; var JSONStr=JSON.stringify(book,null,"- - "); 最终结果如下: { - - "title": "book title", - - "authors": [ - - - - "one", - - - - "two", - - - - "three" - - ], - - "edition": 2, - - "year": 2016 } JSON.stringify() 也在结果字符串中插入了换行符以提高可读性。只要传入有效的控制缩进的参数值,结果字符串就会包含换行符。最大缩进空格数为 10,所有大于 10 的值都会自动转换为 10 缩进字符串最长不能超过 10 个字符长。如果字符串长度超过了 10 个,结果中将只出现前 10 个字符。 ##### toJSON() 方法 var book={ "title":"book title", authors:[ "one", "two", "three" ], edition:2, year:2016, toJSON:function (){ return this.title; } }; var JSONStr=JSON.stringify(book); console.log(JSONStr);//"book title" console.log(typeof JSONStr);//string > 可以给对象定义 toJSON() 方法,返回其自身的 JSON 数据格式。 > toJSON() 可以作为函数过滤器的补充,因此理解序列化的内部顺序十分重要。 假设把一个对象传入 JSON.stringify() ,序列化该对象的顺序如下。 - (1) 如果存在 toJSON() 方法而且能通过它取得有效的值,则调用该方法。否则,返回对象本身。 - (2) 如果提供了第二个参数,应用这个函数过滤器。传入函数过滤器的值是第(1)步返回的值。 - (3) 对第(2)步返回的每个值进行相应的序列化。 - (4) 如果提供了第三个参数,执行相应的格式化。 无论是考虑定义 toJSON() 方法,还是考虑使用函数过滤器,亦或需要同时使用两者,理解这个顺序都是至关重要的。 # 解析选项 ( JSON.parse()的深入理解 ) var JSONStr = '{"title": "标题","authors": ["one","two","rhree"],"edition": 3,"year": 2016,"releaseDate": "2016,10,08 12:59:01"}'; var JSONObj1 = JSON.parse(JSONStr); console.log(JSONObj1); console.log(typeof JSONObj1);//object var JSONObj2 = JSON.parse(JSONStr,function(key,value){ if(key === "releaseDate"){ return new Date(value); }else{ return value; } }); console.log(JSONObj2); console.log(typeof JSONObj2);//object console.log(JSONObj2.releaseDate.getFullYear()+" - "+JSONObj2.releaseDate.getMinutes());//2016 - 59 结果就是 JSONObj2.releaseDate 属性中会保存一个 Date 对象。正因为如此,才能基于这个对象调用getFullYear() 等方法。