JSON.parse 方法一直以为一直就这么用了,大致也知道它对于字符串格式比较严格,即便有时候可以通过编译器解释器,但是却若非标准 json 格式就无法转成 object。这次也因为后端一个奇怪的返回,我决定好好搞懂它。
# 代码验证
首先,依据是老规矩,代码验证,因为后端返回的三种格式分别如下:
- '["sdalkjald"]'
- '{"attachment":"请上传开发自测报告"}'
- "服务器错误"
于是,为了能兼容 3 者,我作了如下方法进行处理,
var a = '["sdalkjald"]',
b = '{"attachment":"请上传开发自测报告"}',
c = "服务器错误",
p = (e)=>{
try {
let obj = JSON.parse(e);
if (Array.isArray(obj)) {
return obj[0];
}
if (typeof obj == "object") {
console.log(`"${e}"为正常 json 对象`);
return obj;
}
return e;
} catch (err) {
console.log(`"${e}"无法被解析`);
return e;
}
};
console.log(typeof h(a));
console.log(typeof h(b));
console.log(typeof h(c));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
执行结果:
"["sdalkjald"]"解析为数组
string
"{"attachment":"请上传开发自测报告"}"解析为对象
object
"服务器错误"无法被解析
string
1
2
3
4
5
6
2
3
4
5
6
一切似乎都在预期内,但是我总感觉,事情没这么简单。
本着折腾本腾的使命感,我打算继续塞些奇奇怪怪的参数进去跑一跑,为了方便测试,我对上述代码进行了改造
var testArr = [
'["hello"]',
["hello"],
'{"attachment":"请上传开发自测报告"}',
'{"attachment":"请上传开发自测报告",}',
"服务器错误",
"true",
false,
123,
"123",
{},
"{}"
],
p = (e) => {
try {
let obj = JSON.parse(e);
if (Array.isArray(obj)) {
console.log(`${e} => 数组`)
return;
}
switch (typeof obj) {
case "object":
case "number":
case "boolean":
console.log(`${e} => ${typeof e}`)
return;
}
} catch (err) {
// 对象或者字符串将无法通过 JSON.parse
console.log(`${e} => 无法被解析`)
return;
}
};
testArr.forEach((e) => p(e));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
为了方便对应,我直接给出了对照
参数 | 元数据类型 | 返回结果 | 是否能被解析 |
---|---|---|---|
'["hello"]' | string | object(array) | √ |
["hello"] | object(array) | - | |
'{"attachment":"请上传开发自测报告",}' | string | - | |
'{"attachment":"请上传开发自测报告"}' | string | object | √ |
"服务器错误" | string | - | |
"true" | string | boolean | √ |
false | boolean | boolean | √ |
123 | number | number | √ |
"123" | string | number | √ |
{} | object | - | |
"{}" | string | object | √ |
# 结论
对象、以及非特殊字符串无法被 JSON.parse()
解析到;
这让我想到了 eval()
,两者对于绝大多数的解析几乎一致;
将测试方法中的 JSON.parse(e)
更为 eval("("+e+")")
后,可以达到对照表
参数 | 元数据类型 | 返回结果 | 是否能被解析 | 是否与 JSON.parse() 结果一致 |
---|---|---|---|---|
'["hello"]' | string | object(array) | √ | √ |
["hello"] | object(array) | - | √ | |
'{"attachment":"请上传开发自测报告",}' | string | object | √ | |
'{"attachment":"请上传开发自测报告"}' | string | object | √ | √ |
"服务器错误" | string | - | √ | |
"true" | string | boolean | √ | √ |
false | boolean | boolean | √ | √ |
123 | number | number | √ | √ |
"123" | string | number | √ | √ |
{} | object | - | √ | |
"{}" | string | object | √ | √ |
可以看到,两者确实存在极其相似的特性。
区别主要在于,字符串除了标准 json
串、 number
和 boolean
以外,非标准 json
串,亦可以解析。
那么是否 eval()
比 JSON.parse()
更佳呢?
答案是不能!
var G = '{ "title" : "some title" , "text" : window.location.href="https://www.baidu.com" }';
1
执行上述语句,可以看到,页面甚至跳转到了百度
所以,当数据来源无法可信时,使用 eval
并不安全。
so...