美文网首页
OpensResty踩坑数组为空转为json时为什么变成了obj

OpensResty踩坑数组为空转为json时为什么变成了obj

作者: 幕若愚 | 来源:发表于2021-04-18 23:03 被阅读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

    相关文章

      网友评论

          本文标题:OpensResty踩坑数组为空转为json时为什么变成了obj

          本文链接:https://www.haomeiwen.com/subject/jalblltx.html