解析与序列化
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 对象有两个方法: 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() 除了要序列化的 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 个字符。
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() 方法,还是考虑使用函数过滤器,亦或需要同时使用两者,理解这个顺序都是至关重要的。
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() 等方法。