|
| 1 | +**解析与序列化** |
| 2 | + |
| 3 | +JSON 之所以流行,拥有与 JavaScript 类似的语法并不是全部原因,更重要的一个原因是,可以很容易把JSON 数据结构解析为有用的 JavaScript 对象 |
| 4 | + |
| 5 | +bookThreeTitle=books[2].title |
| 6 | + |
| 7 | +下面是在 DOM结构中查找数据的代码: |
| 8 | + |
| 9 | +bookThreeTitle=loadXMLDoc("books.xml").getElementsByTagName("book")[2].getAttribute("title") |
| 10 | + |
| 11 | +看看这些多余的方法调用,就不难理解为什么 JSON 能得到 JavaScript 开发人员的热烈欢迎了。从s此以后,JSON 就成了 Web 服务开发中交换数据的事实标准。 |
| 12 | + |
| 13 | +- JSON对象 |
| 14 | +- 序列化选项 |
| 15 | +- 解析选项 |
| 16 | + |
| 17 | +# JSON对象 |
| 18 | + |
| 19 | +JSON 对象有两个方法: stringify() 和 parse() 。在最简单的情况下,这两个方法分别用于把JavaScript 对象序列化为 JSON 字符串和把 JSON 字符串解析为原生 JavaScript 值。 |
| 20 | + |
| 21 | +也可以使用JavaScript 的 eval() 函数来解析JSON字符串,但是使用 eval() 对 JSON 数据结构求值存在风险,因为可能会执行一些恶意代码。还有就是比较好性能;(eval可以解析任何字符串,属于比较底层的方法,虽然通杀但是一般不会使用;) |
| 22 | + |
| 23 | +**JSON.stringify的用法**,JSON对象转为JSON字符串 |
| 24 | + |
| 25 | + var JSONObj = { |
| 26 | + title: "Professional JavaScript", |
| 27 | + authors: [ |
| 28 | + "Nicholas C. Zakas" |
| 29 | + ], |
| 30 | + edition: 3, |
| 31 | + year: 2011, |
| 32 | + testUndefined:undefined, |
| 33 | + testFunction:function(){return 2+2} |
| 34 | + }; |
| 35 | + var JSONStr = JSON.stringify(JSONObj); |
| 36 | + console.log(JSONStr);//{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011} |
| 37 | + console.log(typeof JSONObj);//object |
| 38 | + console.log(typeof JSONStr);//string |
| 39 | + |
| 40 | +默认情况下, JSON.stringify() 输出的 JSON 字符串不包含任何空格字符或缩进, |
| 41 | + |
| 42 | +在序列化 JavaScript 对象时,所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined 的任何属性也都会被跳过。结果中最终都是值为有效 JSON 数据类型的实例属性。 |
| 43 | + |
| 44 | +上面的 **testUndefined*** 和 **testFunction** 将不会出现最终的结果中; |
| 45 | + |
| 46 | + |
| 47 | +**JSON.parse的用法**,JSON字符串转为JSON对象; |
| 48 | + |
| 49 | + var JSONStr1 = '{title: "Professional JavaScript",authors: ["Nicholas C. Zakas"],edition: 3,year: 2011}'; |
| 50 | + var JSONStr2 = '{"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],"edition": 3,year: 2011}'; |
| 51 | + var JSONStr3 = '{"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],"edition": 3,"year": 2011}'; |
| 52 | + var JSONStr4 = '{"title": "Professional JavaScript","authors": ["Nicholas C. Zakas"],"edition": 3,"year": 2011,"testUndefined":undefined}'; |
| 53 | + |
| 54 | + //var JSONObj1 = JSON.parse(JSONStr1); // 报错啦,属性值没有"" |
| 55 | + //var JSONObj2 = JSON.parse(JSONStr2); // 报错啦,属性值没有全部用"" |
| 56 | + var JSONObj3 = JSON.parse(JSONStr3); // 成功 |
| 57 | + //var JSONObj4 = JSON.parse(JSONStr4); // 报错啦,不能含有undefined,function等数据 |
| 58 | + console.log(JSONObj3);//{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011} |
| 59 | + console.log(typeof JSONObj3);//object |
| 60 | + |
| 61 | +> 如果传给 JSON.parse() 的字符串不是有效的 JSON,该方法会抛出错误。 |
| 62 | +
|
| 63 | +# 序列化选项 ( JSON.stringify()的深入理解 ) |
| 64 | + |
| 65 | +实际上, JSON.stringify() 除了要序列化的 JavaScript 对象外,还可以接收另外两个参数,这两个参数用于指定以不同的方式序列化 JavaScript 对象。第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在 JSON 字符串中保留缩进。单独或组合使用这两个参数,可以更全面深入地控制 JSON 的序列化 |
| 66 | + |
| 67 | +JSON.stringify(jsonObj,**arguments1**,**arguments2**) |
| 68 | + |
| 69 | +- arguments1 过滤器的作用,用来过滤结果; |
| 70 | +- arguments2 字符串缩进的作用; |
| 71 | + |
| 72 | +##### 过滤器(用来过滤结果) |
| 73 | + |
| 74 | +- **数组过滤** |
| 75 | + |
| 76 | + var book={ |
| 77 | + "title":"book title", |
| 78 | + authors:[ |
| 79 | + "one", |
| 80 | + "two", |
| 81 | + "three" |
| 82 | + ], |
| 83 | + edition:2, |
| 84 | + year:2016 |
| 85 | + }; |
| 86 | + var JSONStr=JSON.stringify(book,["title","year","other"]); |
| 87 | + console.log(JSONStr);//{"title":"book title","year":2016} |
| 88 | + console.log(typeof JSONStr);//string |
| 89 | + |
| 90 | +JSON.stringify() 的第二个参数是一个数组,其中包含三个字符串: "title"、"year"、"other"。这两个属性与将要序列化的对象中的属性是对应的,因此在返回的结果字符串中,就只会包含这两个属性;因为源数据中没有other这个键值对;匹配不到,就会忽略掉; |
| 91 | + |
| 92 | +- **函数过滤** |
| 93 | + |
| 94 | + var book={ |
| 95 | + "title":"book title", |
| 96 | + authors:[ |
| 97 | + "one", |
| 98 | + "two", |
| 99 | + "three" |
| 100 | + ], |
| 101 | + edition:2, |
| 102 | + year:2016 |
| 103 | + }; |
| 104 | + var JSONStr=JSON.stringify(book,function(key,value){ |
| 105 | + switch (key){ |
| 106 | + case "authors": |
| 107 | + return value.join("+++++");//如果键为 "authors" ,就将数组连接为一个字符串; |
| 108 | + case "year": |
| 109 | + return 5000+"change";//如果键为 "year" ,则将其值设置为 5000change ; |
| 110 | + case "edition": |
| 111 | + return undefined;//如果键为 "edition" ,通过返回 undefined 删除该属性 |
| 112 | + default : |
| 113 | + return value;//最后,一定要提供 default 项,此时返回传入的值,以便其他值都能正常出现在结果中。 |
| 114 | + } |
| 115 | + }); |
| 116 | + console.log(JSONStr);//{"title":"book title","authors":"one+++++two+++++three","year":"5000change"} |
| 117 | + console.log(typeof JSONStr);//string |
| 118 | + |
| 119 | +这里,函数过滤器根据传入的键来决定结果 |
| 120 | + |
| 121 | +Firefox 3.5 和 3.6 对 JSON.stringify() 的实现有一个 bug,在将函数作为该方法的第二个参数时这个 bug 就会出现,即这个函数只能作为过滤器:返回 undefined 意味着要跳过某个属性,而返回其他任何值都会在结果中包含相应的属性。Firefox 4 修复了这个 bug。(未亲自验证) |
| 122 | + |
| 123 | +##### 字符串缩进 |
| 124 | + |
| 125 | + var book={ |
| 126 | + "title":"book title", |
| 127 | + authors:[ |
| 128 | + "one", |
| 129 | + "two", |
| 130 | + "three" |
| 131 | + ], |
| 132 | + edition:2, |
| 133 | + year:2016 |
| 134 | + }; |
| 135 | + var JSONStr=JSON.stringify(book,null,4); |
| 136 | + console.log(JSONStr); |
| 137 | + console.log(typeof JSONStr);//string |
| 138 | + |
| 139 | +输出的结果如下: |
| 140 | + |
| 141 | + { |
| 142 | + "title": "book title", |
| 143 | + "authors": [ |
| 144 | + "one", |
| 145 | + "two", |
| 146 | + "three" |
| 147 | + ], |
| 148 | + "edition": 2, |
| 149 | + "year": 2016 |
| 150 | + } |
| 151 | + |
| 152 | +**也可以用字符替换空格**; |
| 153 | + |
| 154 | + var JSONStr=JSON.stringify(book,null,"- - "); |
| 155 | + |
| 156 | +最终结果如下: |
| 157 | + |
| 158 | + { |
| 159 | + - - "title": "book title", |
| 160 | + - - "authors": [ |
| 161 | + - - - - "one", |
| 162 | + - - - - "two", |
| 163 | + - - - - "three" |
| 164 | + - - ], |
| 165 | + - - "edition": 2, |
| 166 | + - - "year": 2016 |
| 167 | + } |
| 168 | + |
| 169 | +JSON.stringify() 也在结果字符串中插入了换行符以提高可读性。只要传入有效的控制缩进的参数值,结果字符串就会包含换行符。最大缩进空格数为 10,所有大于 10 的值都会自动转换为 10 |
| 170 | + |
| 171 | +缩进字符串最长不能超过 10 个字符长。如果字符串长度超过了 10 个,结果中将只出现前 10 个字符。 |
| 172 | + |
| 173 | +##### toJSON() 方法 |
| 174 | + |
| 175 | + var book={ |
| 176 | + "title":"book title", |
| 177 | + authors:[ |
| 178 | + "one", |
| 179 | + "two", |
| 180 | + "three" |
| 181 | + ], |
| 182 | + edition:2, |
| 183 | + year:2016, |
| 184 | + toJSON:function (){ |
| 185 | + return this.title; |
| 186 | + } |
| 187 | + }; |
| 188 | + var JSONStr=JSON.stringify(book); |
| 189 | + console.log(JSONStr);//"book title" |
| 190 | + console.log(typeof JSONStr);//string |
| 191 | + |
| 192 | +> 可以给对象定义 toJSON() 方法,返回其自身的 JSON 数据格式。 |
| 193 | +> toJSON() 可以作为函数过滤器的补充,因此理解序列化的内部顺序十分重要。 |
| 194 | +
|
| 195 | +假设把一个对象传入 JSON.stringify() ,序列化该对象的顺序如下。 |
| 196 | +- (1) 如果存在 toJSON() 方法而且能通过它取得有效的值,则调用该方法。否则,返回对象本身。 |
| 197 | +- (2) 如果提供了第二个参数,应用这个函数过滤器。传入函数过滤器的值是第(1)步返回的值。 |
| 198 | +- (3) 对第(2)步返回的每个值进行相应的序列化。 |
| 199 | +- (4) 如果提供了第三个参数,执行相应的格式化。 |
| 200 | + |
| 201 | +无论是考虑定义 toJSON() 方法,还是考虑使用函数过滤器,亦或需要同时使用两者,理解这个顺序都是至关重要的。 |
| 202 | + |
| 203 | +# 解析选项 ( JSON.parse()的深入理解 ) |
| 204 | + |
| 205 | + var JSONStr = '{"title": "标题","authors": ["one","two","rhree"],"edition": 3,"year": 2016,"releaseDate": "2016,10,08 12:59:01"}'; |
| 206 | + var JSONObj1 = JSON.parse(JSONStr); |
| 207 | + console.log(JSONObj1); |
| 208 | + console.log(typeof JSONObj1);//object |
| 209 | + |
| 210 | + var JSONObj2 = JSON.parse(JSONStr,function(key,value){ |
| 211 | + if(key === "releaseDate"){ |
| 212 | + return new Date(value); |
| 213 | + }else{ |
| 214 | + return value; |
| 215 | + } |
| 216 | + }); |
| 217 | + console.log(JSONObj2); |
| 218 | + console.log(typeof JSONObj2);//object |
| 219 | + |
| 220 | + console.log(JSONObj2.releaseDate.getFullYear()+" - "+JSONObj2.releaseDate.getMinutes());//2016 - 59 |
| 221 | + |
| 222 | +结果就是 JSONObj2.releaseDate 属性中会保存一个 Date 对象。正因为如此,才能基于这个对象调用getFullYear() 等方法。 |
0 commit comments