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

上一篇下一篇

猜你喜欢

热点阅读