-
Notifications
You must be signed in to change notification settings - Fork 175
Json
Bruce edited this page Oct 1, 2023
·
4 revisions
Lua的json库主要是对table进行encode和decode, 先列举以下lua和json的差异
- Lua中只有一种数据结构
table
, 而json
的定义是区分数组和对象的 - Lua中table的key可以是integer类型, 而json对象的key只能是string类型
对于第一个差异, 主要在于table
没有一个明确的标识来区分它是array
还是hash
,这就需要做一些推断。
下面结合常见的lua-json库来说明这点:
local rapidjson = require("rapidjson")
local cjson = require("cjson")
local integer_key_table1 = {
[1] = "a",
[2] = "b",
[100] = "c",
}
local integer_key_table2 = {
[101] = "a",
[102] = "b",
[103] = "c",
}
print("rapidjson outout1:", rapidjson.encode(integer_key_table1))
print("cjson outout1:", pcall(cjson.encode,integer_key_table1))
print("rapidjson outout2:", rapidjson.encode(integer_key_table2))
print("cjson outout2:", pcall(cjson.encode,integer_key_table2))
--[[
rapidjson outout1: ["a","b"]
cjson outout1: false Cannot serialise table: excessively sparse array
rapidjson outout2: {}
cjson outout2: false Cannot serialise table: excessively sparse array
]]
可见对于这种情况,rapidjson和cjson库表现都不好。moon中的json库解决了这个问题:采用了少量代价,来检测它是array
还是hash
。
static inline size_t array_size(lua_State* L, int index)
{
// test first key
lua_pushnil(L);
if (lua_next(L, index) == 0) // empty table
return 0;
lua_Integer firstkey = lua_isinteger(L, -2) ? lua_tointeger(L, -2) : 0;
lua_pop(L, 2);
if (firstkey <= 0)
{
return 0;
}
else if (firstkey == 1)
{
/*
* https://www.lua.org/manual/5.4/manual.html#3.4.7
* The length operator applied on a table returns a border in that table.
* A border in a table t is any natural number that satisfies the following condition :
* (border == 0 or t[border] ~= nil) and t[border + 1] == nil
*/
auto len = (lua_Integer)lua_rawlen(L, index);
lua_pushinteger(L, len);
if (lua_next(L, index)) // has more fields?
{
lua_pop(L, 2);
return 0;
}
return len;
}
auto len = (lua_Integer)lua_rawlen(L, index);
if (firstkey > len)
return 0;
lua_pushnil(L);
while (lua_next(L, index) != 0)
{
if (lua_isinteger(L, -2))
{
lua_Integer x = lua_tointeger(L, -2);
if (x > 0 && x <= len)
{
lua_pop(L, 1);
continue;
}
}
lua_pop(L, 2);
return 0;
}
return len;
}
结果测试:
local json = require("json")
local integer_key_table = {
[1] = "a",
[2] = "b",
[100] = "c",
}
print("moonjson outout:", json.encode(integer_key_table))
--- moonjson outout: {"1":"a","2":"b","100":"c"}
这里就解决了encode无法区分array
还是hash
的问题。
但decode时又有另一个问题:Json的key都是string类型,造成decode 后原本的lua integer-key 变成 string-key, 我使用了如下方案解决了这个问题:
lua变量不能是 -,0-9 开头,json key decode时,先检测第一个字符是否是-,0-9 开头,如果是,就说明这是一个整型key。
local json = require("json")
local integer_key_table = {
[1] = "a",
[2] = "b",
[100] = "c",
}
local str = json.encode(integer_key_table)
print_r(json.decode(str))
--[[
{
[1] = "a",
[2] = "b",
[100] = "c",
}
]]
这种方案有一些限制:
decode时无法区分{["1"]="a",["2"]="b"}
这种格式