JSON作为数据交换协议应用已越来越广,其简易性和编程友好性使得程序员们几乎无不适之感,也因此全面盖过xml成为前后端数据交换的通用格式之一。我们的搜索API也将其作为基本数据交换格式提供接口服务。但前几天发生个有意思的事情,谨记之。
前几天和前端同事开发H5项目,前端同事提出个听上去挺合理的建议,
其使用的接口数据需要后端把json协议中的某个键值对集合按序组装,
这样前端可以按序取出,不需要额外判断。形如:
{
1th:'abc',
2nd:'cbd',
3rd:'efg'
}
作为非nb程序员,我的第一反应是这样是不OK的,因为集合本身是无序的,但该同事用了js做了个简单的示范,证实js封装/解析json的集合节点时确实是按序封装/解析的。呃呃呃,我当时就给JS跪了,太人性了不是,为了更好地说服这位小哥,打算做个实验并辅以协议本身说明以证实我的观点。
于是分别用了python和lua两种语言验证之
- 用python验证json序列化字典节点时的默认顺序
1 | import json data = { 'name' : 'ACME', 'shares' : 100, 'price' : 542.23 } json_str = json.dumps(data) print json_str |
其对应的输出为:
[ ~]$ python t.py
{“price”: 542.23, “name”: “ACME”, “shares”: 100}
并没有按我们预期的顺序来
- 再用lua验证一下,这两种是我们主要的后台脚本语言,以代表说明
cjson = require(“cjson”)
a = {}
a.x = 1
a.y = 2
print(cjson.encode(a))
非常遗憾的是,lua语言也没有按声明顺序去序列化该结构,输出为:
[ ~]$ lua t.lua
{“y”:2,”x”:1}
由上试验可知,json协议并没有强制要求字典结构的序列化需按声明顺序来,python,lua均按照自己的语言方式来组织
json协议也明确地说明了这一点:
对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,
“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);
“‘名称/值’ 对”之间使用“,”(逗号)分隔。
python和lua等语言在实现该协议时,采用的顺序和内置的字典对象的遍历顺序是一样的。像python的字典对象用的是hash表实现,其采用的遍历顺序依具体实现而定,甚至不同版本的python的字典对象遍历顺序都是不一样的。而lua的字典也是如此,用的底层数据结构也是hash表,自然也很难断定顺序。
而js这个时候就跳出来了,按编写的顺序编码解码,这真是有点意思,前端同事查到一段解释,基本上主流浏览器是按序解析的,但并无保证,只是巧合
Currently all major browsers loop over the properties of an object
in the order in which they were defined. Chrome does this as well,
except for a couple cases.
至此,这个小建议就只能告吹,但因此而来的一些小思考留了下来,我们不能断想程序是按着我们的实现来的,是否合理顺情其实也是编程思考的重要根据之一。另外知道了底层实现也就自然能理解其无序性,基本上字典{}这种结构是保证不了顺序的,因其底层基本是用无序容器实现。哪怕是用红黑树之类的有序容器实现,也最多保证key的默认顺序(字典序,数值序,etc),并不能保证按代码编写的顺序来定。
参考
http://www.json.org/json-zh.html
http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop