OpensResty踩坑数组为空转为json时为什么变成了obj
2021-04-18 本文已影响0人
幕若愚
大家在用OpenResty写项目时肯定会写到类似这样的逻辑
local function produceData(param)
local datas = {}
if param == 1 then
for i=1,3 do
datas[i] = {["index"] = i}
end
print(cjson.encode(datas))
else
print(cjson.encode(datas))
end
return datas
end
当执行produceData(1)时,很显然会打印出符合我们预期的一组数据
[
{
"index": 1
},
{
"index": 2
},
{
"index": 3
}
]
当其他的小伙伴用Go语言调用使用这个逻辑实现的接口时(produceData(0)),竟然报错了
unmarshal err: json: cannot unmarshal object into Go value of type []map[string]string
这个错误明显是在Go里面获得数据后处理json时与定义的类型不一致导致的
我在OpenRestry里打印了下produceData(0)时的结果
{}
难怪报错,这种情况下在OpenResty里你自认为的空数组转为json后竟然是空对象。其实在lua里是把数组和字典融合到一起了,所以是无法区分空数组和空字典的,都为{}。和强类型语言(c/c++、java、go等)做交互时肯定是有问题的。
调查发现,cjson库中通过设置encode_empty_table_as_object来得到精确的空table的json类型。但经过多次测试发现cjson设置encode_empty_table_as_object是产生的全局的影响的,你自己新写的项目还好,可以按照这种思路去控制json的encode结果。但如果是改老项目,也许有的地方的逻辑就是期望空table转为“{}”呢。因此我封装了两个方法来应对这种情况。
local _M = {}
local cjson = require "cjson.safe"
-- 可以自由设置cjson encode时空table是否转成object
-- 该值为false时,空table会解析成[]
function _M.encode(data, empty_table_as_object)
local json_value = nil
if cjson.encode_empty_table_as_object then
cjson.encode_empty_table_as_object(empty_table_as_object or false) -- 设置空的table解析为[]
end
pcall(function ()
json_value = cjson.encode(data)
end, data)
return json_value
end
-- 将空table会解析成[],解析完成后再设置会为默认值
-- 因为cjson的encode_empty_table_as_object是全局生效的
-- 可以避免影响其他地方直接使用cjson.encode且期望空table为{}的逻辑
function _M.encode_empty_as_array(data)
local json_value = nil
cjson.encode_empty_table_as_object(false) -- 设置空的table解析为[]
pcall(function ()
json_value = cjson.encode(data)
end, data)
cjson.encode_empty_table_as_object(true) -- 恢复为默认设置
return json_value
end
return _M
这样在适当的地方调用适当的方法就可以解决相关的问题了。
参考:
https://www.kancloud.cn/kancloud/openresty-best-practices/50390