Skip to content

Latest commit

 

History

History
222 lines (171 loc) · 8.69 KB

解析与序列化.md

File metadata and controls

222 lines (171 loc) · 8.69 KB

解析与序列化

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