111 lines
2.7 KiB
Lua
111 lines
2.7 KiB
Lua
--[[
|
|
Licensed according to the included 'LICENSE' document
|
|
Author: Thomas Harning Jr <harningt@gmail.com>
|
|
]]
|
|
local jsonutil = require("json.util")
|
|
|
|
local type = type
|
|
local pairs = pairs
|
|
local assert = assert
|
|
|
|
local table = require("table")
|
|
local math = require("math")
|
|
local table_concat = table.concat
|
|
local math_floor, math_modf = math.floor, math.modf
|
|
|
|
local jsonutil = require("json.util")
|
|
local util_IsArray = jsonutil.IsArray
|
|
|
|
local _ENV = nil
|
|
|
|
local defaultOptions = {
|
|
isArray = util_IsArray
|
|
}
|
|
|
|
local modeOptions = {}
|
|
|
|
local function mergeOptions(options, mode)
|
|
jsonutil.doOptionMerge(options, false, 'array', defaultOptions, mode and modeOptions[mode])
|
|
end
|
|
|
|
--[[
|
|
Utility function to determine whether a table is an array or not.
|
|
Criteria for it being an array:
|
|
* ExternalIsArray returns true (or false directly reports not-array)
|
|
* If the table has an 'n' value that is an integer >= 1 then it
|
|
is an array... may result in false positives (should check some values
|
|
before it)
|
|
* It is a contiguous list of values with zero string-based keys
|
|
]]
|
|
local function isArray(val, options)
|
|
local externalIsArray = options and options.isArray
|
|
|
|
if externalIsArray then
|
|
local ret = externalIsArray(val)
|
|
if ret == true or ret == false then
|
|
return ret
|
|
end
|
|
end
|
|
-- Use the 'n' element if it's a number
|
|
if type(val.n) == 'number' and math_floor(val.n) == val.n and val.n >= 1 then
|
|
return true
|
|
end
|
|
local len = #val
|
|
for k,v in pairs(val) do
|
|
if type(k) ~= 'number' then
|
|
return false
|
|
end
|
|
local _, decim = math_modf(k)
|
|
if not (decim == 0 and 1<=k) then
|
|
return false
|
|
end
|
|
if k > len then -- Use Lua's length as absolute determiner
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--[[
|
|
Cleanup function to unmark a value as in the encoding process and return
|
|
trailing results
|
|
]]
|
|
local function unmarkAfterEncode(tab, state, ...)
|
|
state.already_encoded[tab] = nil
|
|
return ...
|
|
end
|
|
local function getEncoder(options)
|
|
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
|
local function encodeArray(tab, state)
|
|
if not isArray(tab, options) then
|
|
return false
|
|
end
|
|
-- Make sure this value hasn't been encoded yet
|
|
state.check_unique(tab)
|
|
local encode = state.encode
|
|
local compositeEncoder = state.outputEncoder.composite
|
|
local valueEncoder = [[
|
|
for i = 1, (composite.n or #composite) do
|
|
local val = composite[i]
|
|
PUTINNER(i ~= 1)
|
|
val = encode(val, state)
|
|
val = val or ''
|
|
if val then
|
|
PUTVALUE(val)
|
|
end
|
|
end
|
|
]]
|
|
return unmarkAfterEncode(tab, state, compositeEncoder(valueEncoder, '[', ']', ',', tab, encode, state))
|
|
end
|
|
return { table = encodeArray }
|
|
end
|
|
|
|
local array = {
|
|
mergeOptions = mergeOptions,
|
|
isArray = isArray,
|
|
getEncoder = getEncoder
|
|
}
|
|
|
|
return array
|