问题
如果需要发给后台一个标准的JSON数组
,通常情况下使用JSON.stringify()
对数组进行序列化即可,如果数组比较简单,也可以使用引号手动拼接。在项目开发过程中遇到过这样一个问题,在手动拼接JSON数组
后,经过umi-request的自动序列化与反序列化,最终发送的请求参数却不是一个可识别的标准JSON数组
,而是一个字符串。主要原因在于:对嵌套字符串进行序列化和反序列化时,有可能会将字符串中的引号进行转义,如果拼接的字符串格式不正确,就会出现无法转义的情况,最终只能得到一个字符串而不是JSON数组
。
字符串中的单/双引号
在讨论上面的问题前,先看一字符串的单引号和双引号的区别。在JavaScript中单引号和双引号可以同时使用,混合使用时要遵循一定的准则:
- 双引号
""
中可以嵌套单引号''
- 单引号
''
中可以嵌套双引号""
- 双引号中也可以嵌套双引号,但必须使用转义
\"
- 单引号中也可以嵌套单引号,但必须使用转义
\'
1 | var a = 'JavaScript "is" awesome'; |
理论上来说,不同类型引号之间可以通过转义无限嵌套,但由于可读性太差,一般不建议这么做。
JSON序列化和反序列化时出现的问题
回到一开始在项目中遇到的问题,如果使用嵌套字符串手动拼接JSON数组
,应该如何使用?
1 | // 单引号内嵌套双引号 |
这个结果正好是想要的标准json数组
。
换另外一种形式,在双引号内嵌套单引号
1 | // 双引号内嵌套单引号 |
request payload则是:
从结果上看来,请求参数是一个JSON字符串
而不是JSON数组
。
通过上面讲述的字符串嵌套规则,在两种方式都支持的情况下,出现问题的原因应该在于umi-request的JSON自动序列化与反序列化中的某一过程。
部分猜测及验证
根据以上猜测,直接使用JSON.stringify()
和JSON.parse()
进行验证:
1 | var a = JSON.stringify('["salvo"]'); // ""[\"salvo\"]"" |
以上可以看出,JSON序列化'["salvo"]'
时,首先将内层的双引号转义,然后将外层的单引号序列化为双引号,以便反序列化时正确解析;在序列化"['salvo']"
时,由于内层使用单引号不符合标准的JSON数组
格式,因此没有对其进行转义或者其他处理,然后将双引号包裹的内容当成了一个完整的字符串。至于在序列化和反序列化时,解析内外层的顺序,使用umi内置的格式化标准以及prettier工具验证如下:
1 | "[\"salvo\"]” // '["salvo"]' |
可以看出prettier在格式化时,会以内层的引号类型为准,然后在外层采用不同的类型。
结论及疑问
JSON序列化与反序列化时,遵循的是ECMA262的相关标准,在操作嵌套字符串时是根据JavaScript来具体实现的。因此在使用字符串拼接JSON数组
时,不仅要符合字符串的嵌套规则,更要遵循JSON的标准数据格式。
当然,为防止这种问题的出现,应当直接使用JSON.stringify()
创建JSON数组
,避免偷懒直接使用字符串进行拼接。
疑问在于:从结果上看,JSON的序列化/反序列化与嵌套字符串使用prettier格式化后的效果是相同的,至于是不是对于相同标准的不同实现,我暂时还没有找到相关的资料并验证。