如何在Redis中使用cmsgpack和Lua存储具有空值的JSON?

时间:2020-06-15 11:38:50

标签: json redis lua msgpack

我一直在阅读有关cjson的文档,并且了解空值到cjson.null值的转换(因为无法在Lua表中存储nil。如果我使用cjson.decode和cjson.encode。

但是,当我尝试使用cmsgpack打包和解压缩表的内容时,不存在具有空值的键。我使用的代码与此one类似。

您如何解决此问题?您是否用特殊值替换了null值(在调用cmspack.pack之后再使用{isnull:true},然后再将{isnull:true}替换为null)还是使用任何其他技术?

1 个答案:

答案 0 :(得分:0)

我进行了快速研究,没有找到任何用于处理哈希表中null值的msgpack库。我并不是说这样的库不存在。

我选择了另一种方法。由于lua-cmsgpack在解码MessagePack二进制数据时没有做任何特殊的处理null值的操作,而nil值仅在表中存在问题,因此我们可以为哈希添加特殊情况代码→表解码器

实际上,补丁很简单:

--- a/lua_cmsgpack.c
+++ b/lua_cmsgpack.c
@@ -565,6 +565,10 @@ void mp_decode_to_lua_hash(lua_State *L, mp_cur *c, size_t len) {
         mp_decode_to_lua_type(L,c); /* key */
         if (c->err) return;
         mp_decode_to_lua_type(L,c); /* value */
+        if (lua_isnil(L, -1)) {
+            lua_pop(L, 1);
+            lua_pushlightuserdata(L, NULL);
+        }
         if (c->err) return;
         lua_settable(L,-3);
     }

此处第二次调用mp_decode_to_lua_type将哈希条目 value 转换为相应的Lua类型值,并且当该值为nil时(即{{1} }(在MessagePack中),我们将其替换为简单的用户数据(null指针)。相同的用户数据(NULL指针)用作NULL值。

我们不需要修补编码器,因为它会将包括任何用户数据在内的任何不受支持的值编码为cjson.null,也就是说,您可以使用null用户数据来表示cjson.null值(并且cjson.decode就是这样做的。哈希被解码后,您将获得相同的用户数据。

相同的技巧可以应用于类似数组的表,但是如果您知道表的大小,则不必这样做-您可以从第一个索引到最后一个索引进行迭代。

null

输出:

local cjson = require('cjson')
local cmsgpack = require('cmsgpack')

local encoded_hash = cmsgpack.pack({k1 = nil, k2 = cjson.null, k3 = 'baz'})
local decoded_hash = cmsgpack.unpack(encoded_hash)
print('decoded_hash.k2 == cjson.null?', decoded_hash.k2 == cjson.null)
for k, v in pairs(decoded_hash) do
    print(k, '->', v)
end

local encoded_array = cmsgpack.pack({'foo', nil, cjson.null, 'bar'})
local decoded_array = cmsgpack.unpack(encoded_array)
print('#decoded_array =', #decoded_array)
for i = 1, 4 do
    print(i, '->', decoded_array[i])
end

感谢LuaRocks,我们甚至不需要为如此微不足道的补丁支持库的fork。修补程序可以直接包含在rockspec文件中。我创建了具有修改过的rockspec的要点:https://gist.github.com/un-def/d6b97f5ef6b47edda7f65e0c6144663c

您可以使用以下命令安装该库的修补版本:

decoded_hash.k2 == cjson.null?  true
k3  ->  baz
k2  ->  userdata: (nil)
#decoded_array =    4
1   ->  foo
2   ->  nil
3   ->  userdata: (nil)
4   ->  bar