Redis Lua 区分空数组和空对象

我在使用 Redis 3.2 中的脚本设置 JSON 对象中特定值时,在 cjson lua 中遇到了这个 bug。

目前,Redis 中的 lua 没有区分空 JSON 数组和空 JSON 对象。这在序列化内部包含数组的 JSON 对象时会引起严重问题。

eval "local json_str = '{\"items\":[],\"properties\":{}}' return cjson.encode(cjson.decode(json_str))" 0

结果:

"{\"items\":{},\"properties\":{}}"

我找到了这个解决方案 https://github.com/mpx/lua-cjson/issues/11,但我不能在 Redis 脚本中实现它。

这是一个不成功的尝试:

eval

"function cjson.mark_as_array(t)
local mt = getmetatable(t) or {}
mt.__is_cjson_array = true
return setmetatable(t, mt)
end
function cjson.is_marked_as_array(t)
local mt = getmetatable(t)
return mt and mt.__is_cjson_array end
local json_str = '{\"items\":[],\"properties\":{}}'
return cjson.encode(cjson.decode(json_str))"

0

任何帮助或指针都将不胜感激。

点赞
用户11207361
用户11207361

有两个方案。

  1. 修改lua-cjson源码并编译redis,点击这里查看详情。

  2. 通过代码修复:

local now = redis.call("time")
-- local timestamp = tonumber(now[1]) * 1000 + math.floor(now[2]/1000)
math.randomseed(now[2])
local emptyFlag = "empty_" .. now[1] .. "_" .. now[2] .. "_" .. math.random(10000)
local emptyArrays = {}
local function emptyArray()
    if cjson.as_array then
        -- cjson fixed:  https://github.com/xiyuan-fengyu/redis-lua-cjson-empty-table-fix
        local arr = {}
        setmetatable(arr, cjson.as_array)
        return arr
    else
        -- plan 2
        local arr = {}
        table.insert(emptyArrays, arr)
        return arr
    end
end

local function toJsonStr(obj)
    if #emptyArrays > 0 then
        -- plan 2
        for i, item in ipairs(emptyArrays) do
            if #item == 0 then
                -- empty array, insert a special mark
                table.insert(item, 1, emptyFlag)
            end
        end

        local jsonStr = cjson.encode(obj)
        -- replace empty array
        jsonStr = (string.gsub(jsonStr, '%["' .. emptyFlag ..  '"]', "[]"))

        for i, item in ipairs(emptyArrays) do
            if item[1] == emptyFlag then
                table.remove(item, 1)
            end
        end
        return jsonStr
    else
        return cjson.encode(obj)
    end
end

-- example
local arr = emptyArray()
local str = toJsonStr(arr)
print(str) -- "[]"
2019-03-15 08:19:04