正确的序列化 Lua 中带元表的对象
在 Lua 5.2 之后的版本,约定了在元表中可以给出一个 __pairs
方法,而 lua 的基础库 pairs 会使用这个元方法来迭代一个对象。
Lua 5.3 之后的版本,取消了 lua 5.2 中的 __ipairs
约定,而统一使用 lua_geti
来访问整数为索引的数组。
可惜的是,许多 lua 序列化库对此支持的并不好。今天我在改进 bson 的序列化库时,重新考虑了这个问题,看看这个序列化过程怎么做,才能更好的支持 lua 5.3 以后的约定。
在 skynet 中重新实现的 bson 库是这样做的:
json 和 bson 在规范中都区分了数组和字典。
那么,序列化时,应首先判断一个 table 是否有 __len
元方法,如果有,则表示它是一个数组。因为我们不支持把一个需要传入 bson 序列化的 table 同时当成数组和字典使用,如果实现了 __len
元方法,那么显然是希望把它看作一个数组。
然后,判断这个 table 是否有 __pairs
元方法。如果有,这表示它是一个字典,需要用这个元方法迭代它。
如果两个元方法都没有,那么这个 table 是个原生表,需要用额外的方法探测它到底是一个数组还是一个字典。这里采用的方法是,使用 lua_rawlen
获取一下数组部分的长度。然后调用 lua_next
传入最后一个数字索引,探测是否有其它的 key 。
这种方法在 lua 的文档中并没有严格约定,它依赖 lua 的实现。目前官方 lua 的实现中, lua_next
总是先迭代完数组部分,再迭代 hash 部分的。这样实现最为便捷,所以看起来也不会修改。而严格的检测方法则应该是用 lua_next
迭代所有的 key ,逐个判断它们都是否是数字,且是否连续。我不想采用这种开销 O(n) 的严格算法,前面提到的 O(1) 的取巧方法实际工作的很好。
这种,我们对 table 分了三种类别:数组、带元表的字典、原生字典。
数组这个类型不必区分是原生数组还是带元表的,而只需要取出长度(使用 __len
或调用 lua_rawlen
),然后用 lua_geti
逐个调用。如果发现数组中有空洞(value 为 nil ),序列化过程会抛出 error 。(这点对之前的实现是一个改进,老的版本并不能检测出数组中的空洞)
带元表的字典采用 __pairs
方法进行迭代,而原生字典可以直接用 lua_next
迭代。
Comments
sb
Posted by: SSSS | (10) March 29, 2017 02:01 PM
可以 email 联系我。
Posted by: cloud | (9) August 17, 2016 12:45 AM
我是出版社工作人缘,<<lua程序设计>>第四版英文版已经上市,不知云风大侠是否有兴趣为LUA社区做点贡献翻译下该书呢?
Posted by: luaer | (8) August 16, 2016 08:26 PM
如果数组有空洞,lua_rawlen返回的值不确定,这样的话你这个方法貌似不行吧?
Posted by: Anonymous | (7) August 4, 2016 11:05 AM
哦,那这就是 Lua 的惯例了。好久不写 Lua,完全没感觉了……
Posted by: 依云 | (6) June 24, 2016 10:33 AM
@依云
因为对 table 取 # 操作,按 lua 的惯例就是认为 table 是一个 sequence ,取这个 sequence 的长度。
固然你可以用 `__len` 做其它用途,正如你也可以把 `__add` 这些做加法以外的用途,但并没有什么好处。
使用 `__len` 用于模拟一个 sequence 是 lua 基础库已经采用的方案。 如果你真的用 `__len` 记录字典中有多少个元素,就好比你实现 `__add` 不做加法而去做减法。
即没有获得什么好处,还会使 lua 标准库无法正常工作。
table.concat / insert / move / remove / sort / unpack 均依赖 `__len` 的正确定义。
Posted by: Cloud | (5) June 23, 2016 10:10 PM
我记错了,json 也区分了数组。
Posted by: Cloud | (4) June 23, 2016 10:01 PM
为什么「实现了 __len 元方法,那么显然是希望把它看作一个数组」呀?如果这个 __len 是记录这个字典中有多少个元素呢?
Posted by: 依云 | (3) June 23, 2016 09:38 PM
同问为什么说在 JSON 中没有区分数组和字典?
Posted by: 扑来树袋熊 | (2) June 23, 2016 06:57 PM
json 也是严格区分了数组和字典的吧。
我觉得 lua 唯一不好的地方就是不区分数组和字典。它带来的不必要的复杂度。
当然所有 luaer 认为这是侁点。
Posted by: runner-mei | (1) June 23, 2016 06:41 PM