Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redis Lua Differetiating empty array and object

I encountered this bug in cjson lua when I was using a script in redis 3.2 to set a particular value in a json object.

Currently, the lua in redis does not differentiate between an empty json array or an empty json object. Which causes serious problems when serialising json objects that have arrays within them.

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

Result:

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

I found this solution https://github.com/mpx/lua-cjson/issues/11 but I wasn't able to implement in a redis script.

This is an unsuccessful attempt :

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

Any help or pointer appreciated.

like image 449
tanvi Avatar asked Apr 07 '17 08:04

tanvi


1 Answers

There are two plans.

  1. Modify the lua-cjson source code and compile redis, click here for details.

  2. Fix by code:

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) -- "[]"
like image 108
fengyu xiyuan Avatar answered Oct 06 '22 01:10

fengyu xiyuan