我正在开发一个简单的优化JSON函数。 Lua使用表来表示数组,但在JSON中我需要在它们之间进行识别。使用以下代码:
t={
a="hi",
b=100
}
function table2json(t,formatted)
if type(t)~="table" then return nil,"Parameter is not a table. It is: "..type(t) end
local ret=""--return value
local lvl=0 --indentation level
local INDENT=" " --OPTION: the characters put in front of every line for indentation
function addToRet(str) if formatted then ret=ret..string.rep(INDENT,lvl)..str.."\n" else ret=ret..str end end
addToRet("{")
lvl=1
for k,v in pairs(t) do
local typeof=type(v)
if typeof=="string" then
addToRet(k..":\""..v.."\"")
elseif typeof=="number" then
addToRet(k..":"..v)
end
end
lvl=0
addToRet("}")
return ret
end
print(table2json(t,true))
正如您在JSON参考中所看到的,object
在Lua中被称为table
,它与array
不同。
问题是如何检测表是否被用作数组?
任何更简单/更智能的解决方案?
答案 0 :(得分:8)
如果你想要快速,简单,非侵入性的解决方案,它将在大多数的时间内工作,那么我只想检查索引1 - 如果它存在,那么表就是一个数组。当然,没有保证,但根据我的经验,表很少有数字键和其他键。你是否可以将某些对象误认为数组,以及你是否希望这种情况发生,这通常取决于你的使用场景 - 我想这对于一般的JSON库来说并不好。
编辑:对于科学,我去看看Lua CJSON是如何做事的。它遍历所有对并检查所有键是否为整数,同时保持最大键(相关函数为lua_array_length
)。然后它决定是否将表序列化为数组或对象,具体取决于表的稀疏程度(用户控制的比率),即索引为1,2,5,10的表可能会被序列化为数组而指数1,2,1000000将作为对象。我想这实际上是非常好的解决方案。
答案 1 :(得分:4)
没有没有内置的区分方法,因为在Lua中没有区别。
已经存在JSON库,可能已经这样做了(例如。Lua CJSON。
其他选项
__newindex
显式声明数组,以便只允许使用新的数字和后续索引。答案 2 :(得分:3)
@AlexStack
if not t[i] and type(t[i])~="nil" then return false end
如果其中一个元素为false
,则此代码错误,如果失败。
> return isArray({"one", "two"})
true
> return isArray({false, true})
false
我认为整个表达式可以更改为type(t[i]) == nil
但在某些情况下仍会失败,因为它不支持nil值。
我认为,一个好的方法是尝试使用ipairs
或检查#t
是否等于count
,但#t
会返回0对象和{{对于空数组,1}}将为零,因此可能需要在函数开头进行额外检查,例如:count
。
作为旁注,我正在粘贴另一个实现,发现于lua-cjson(由Mark Pulford撰写):
if not next(t) then return true
答案 3 :(得分:3)
区分数组/非数组的最简单算法是:
local function is_array(t)
local i = 0
for _ in pairs(t) do
i = i + 1
if t[i] == nil then return false end
end
return true
end
此处的说明:https://web.archive.org/web/20140227143701/http://ericjmritz.name/2014/02/26/lua-is_array/
那就是说,你仍然会遇到空表问题 - 它们是“数组”还是“哈希”?
对于序列化json的特殊情况,我所做的是用metatable中的字段标记数组。
-- use this when deserializing
local function mark_as_array(t)
setmetatable(t, {__isarray = true})
end
-- use this when serializing
local function is_array(t)
local mt = getmetatable(t)
return mt.__isarray
end
答案 4 :(得分:2)
这是基于Lua特定的#len函数机制的最简单的检查。
function is_array(table)
if type(table) ~= 'table' then
return false
end
-- objects always return empty size
if #table > 0 then
return true
end
-- only object can have empty length with elements inside
for k, v in pairs(table) do
return false
end
-- if no elements it can be array and not at same time
return true
end
local a = {} -- true
local b = { 1, 2, 3 } -- true
local c = { a = 1, b = 1, c = 1 } -- false
答案 5 :(得分:1)
感谢。我开发了以下代码,它可以工作:
---Checks if a table is used as an array. That is: the keys start with one and are sequential numbers
-- @param t table
-- @return nil,error string if t is not a table
-- @return true/false if t is an array/isn't an array
-- NOTE: it returns true for an empty table
function isArray(t)
if type(t)~="table" then return nil,"Argument is not a table! It is: "..type(t) end
--check if all the table keys are numerical and count their number
local count=0
for k,v in pairs(t) do
if type(k)~="number" then return false else count=count+1 end
end
--all keys are numerical. now let's see if they are sequential and start with 1
for i=1,count do
--Hint: the VALUE might be "nil", in that case "not t[i]" isn't enough, that's why we check the type
if not t[i] and type(t[i])~="nil" then return false end
end
return true
end
答案 6 :(得分:1)
您可以简单地测试一下(假设 t
是一个表):
function isarray(t)
return #t > 0 and next(t, #t) == nil
end
print(isarray{}) --> false
print(isarray{1, 2, 3}) --> true
print(isarray{a = 1, b = 2, c = 3}) --> false
print(isarray{1, 2, 3, a = 1, b = 2, c = 3}) --> false
print(isarray{1, 2, 3, nil, 5}) --> true
它测试表的“数组部分”中是否有任何值,然后通过使用带有最后一个连续数字索引的 next
来检查该部分之后是否有任何值。
注意 Lua does some logic 决定何时使用表的这个“数组部分”和“哈希部分”。这就是为什么在最后一个示例中提供的表被检测为数组的原因:尽管中间有 nil
,但它足够密集,可以被视为数组,或者换句话说,它不够稀疏。正如这里的另一个答案提到的,这在数据序列化的上下文中非常有用,您不必自己编程,您可以使用Lua底层逻辑。如果您要序列化最后一个示例,则可以使用 for i = 1, #t do ... end
而不是使用 ipairs
。
根据我在 Lua 和 LuaJIT 实现中的观察,函数 next
总是先查找表的数组部分,因此会在整个数组之后找到任何非数组索引部分,即使之后它不遵循任何特定的顺序。不过,我不确定这在不同 Lua 版本中是否一致。
此外,由您决定将空表也视为数组。在此实现中,它们不被视为数组。您可以将其更改为 return next(t) == nil or (#t > 0 and next(t, #t) == nil)
以执行相反的操作。
无论如何,我想这是在代码行数和复杂性方面你能得到的最短的,因为它的下限是 next
(我认为是 O(1) 或 O(logn))。
答案 7 :(得分:0)
我为漂亮的打印lua表编写了这个函数,并且必须解决同样的问题。这里没有任何解决方案可以解决边缘情况,例如某些键是数字而其他键则不是。这将测试每个索引,看它是否与数组兼容。
function pp(thing)
if type(thing) == "table" then
local strTable = {}
local iTable = {}
local iterable = true
for k, v in pairs(thing) do
--if the key is a string, we don't need to do "[key]"
local key = (((not (type(k) == "string")) and "["..pp(k).."]") or k)
--this tests if the index is compatible with being an array
if (not (type(k) == "number")) or (k > #thing) or(k < 1) or not (math.floor(k) == k) then
iterable = false
end
local val = pp(v)
if iterable then iTable[k] = val end
table.insert(strTable, (key.."="..val))
end
if iterable then strTable = iTable end
return string.format("{%s}", table.concat(strTable,","))
elseif type(thing) == "string" then
return '"'..thing..'"'
else
return tostring(thing)
end
end
答案 8 :(得分:0)
这并不漂亮,而且取决于表的大小和巧妙的欺骗性,它可能会很慢,但在我的测试中,它适用于每种情况:
空表
数字数组
重复数字的数组
带有数字值的字母键
混合数组/非数组
稀疏数组(索引序列中的间隙)
双打表
双打作为键的表
function isarray(tableT)
--has to be a table in the first place of course
if type(tableT) ~= "table" then return false end
--not sure exactly what this does but piFace wrote it and it catches most cases all by itself
local piFaceTest = #tableT > 0 and next(tableT, #tableT) == nil
if piFaceTest == false then return false end
--must have a value for 1 to be an array
if tableT[1] == nil then return false end
--all keys must be integers from 1 to #tableT for this to be an array
for k, v in pairs(tableT) do
if type(k) ~= "number" or (k > #tableT) or(k < 1) or math.floor(k) ~= k then return false end
end
--every numerical key except the last must have a key one greater
for k,v in ipairs(tableT) do
if tonumber(k) ~= nil and k ~= #tableT then
if tableT[k+1] == nil then
return false
end
end
end
--otherwise we probably got ourselves an array
return true
end
非常感谢 PiFace 和 Houshalter,他们的代码大部分都是基于他们的。