2 Licensed according to the included 'LICENSE' document
3 Author: Thomas Harning Jr <harningt@gmail.com>
5 local externalIsArray
= IsArray
-- Support for the special IsArray external function...
7 local tostring, string, type = tostring, string, type
8 local tonumber, math
, assert = tonumber, math
, assert
9 local table, pairs
, ipairs
= table, pairs
, ipairs
10 local getmetatable
, setmetatable
= getmetatable
, setmetatable
14 local null
= require("json.util").null
29 -- Pre-encode the control characters to speed up encoding...
30 -- NOTE: UTF-8 may not work out right w/ JavaScript
31 -- JavaScript uses 2 bytes after a \u... yet UTF-8 is a
32 -- byte-stream encoding, not pairs of bytes (it does encode
33 -- some letters > 1 byte, but base case is 1)
35 local c
= string.char(i
)
36 if c
:match('%c') and not encodingMap
[c
] then
37 encodingMap
[c
] = string.format('\\u%.4X', i
)
40 local function encodeString(s
)
41 return '"' .. string.gsub(s
, '[\\"/%c%z]', encodingMap
) .. '"'
44 local function isArray(val
)
45 if externalIsArray
and externalIsArray(val
) then
48 -- Use the 'n' element if it's a number
49 if type(val
.n
) == 'number' and math
.floor(val
.n
) == val
.n
and val
.n
>= 1 then
53 for k
,v
in pairs(val
) do
54 if type(k
) == 'number' and select(2, math
.modf(k
)) == 0 and 1<=k
then
55 assert(isEncodable(v
), "Invalid array element type:" .. type(v
))
56 if k
> len
then -- Use Lua's length as absolute determiner
59 else -- Not an integral key...
67 local function tonull(val
)
73 -- Forward reference for encodeValue function
75 local alreadyEncoded
-- Table set at the beginning of every
76 -- encoding operation to empty to detect recursiveness
77 local function encodeTable(tab
)
78 if alreadyEncoded
[tab
] then
79 error("Recursive table detected")
81 alreadyEncoded
[tab
] = true
85 for i
= 1,(tab
.n
or #tab
) do
86 retVal
[#retVal
+ 1] = encodeValue(tab
[i
])
88 return '[' .. table.concat(retVal
, ',') .. ']'
91 for i
, v
in pairs(tab
) do
93 if ti
== 'string' or ti
== 'number' or ti
== 'boolean' then
94 i
= encodeString(tostring(i
))
96 error("Invalid object index type: " .. ti
)
98 retVal
[#retVal
+ 1] = i
.. ':' .. encodeValue(v
)
100 return '{' .. table.concat(retVal
, ',') .. '}'
104 local function encodeNumber(number)
105 local str
= tostring(number)
106 if str
== "nan" then return "NaN" end
107 if str
== "inf" then return "Infinity" end
108 if str
== "-inf" then return "-Infinity" end
112 local allowAllNumbers
= true
114 local encodeMapping
= {
115 ['table' ] = encodeTable
,
116 ['number' ] = allowAllNumbers
and encodeNumber
or tostring,
117 ['boolean'] = tostring,
118 ['function'] = tonull
,
119 ['string' ] = encodeString
,
120 ['nil'] = function() return 'null' end -- For the case that nils are encountered count them as nulls
122 function isEncodable(item
)
123 return encodeMapping
[type(item
)] and not (type(item
) == 'function' and item
~= null
)
126 --[[local ]] function encodeValue(item
)
127 local encoder
= encodeMapping
[type(item
)]
129 error("Invalid item to encode: " .. type(item
))
134 function encode(data
)
136 return encodeValue(data
)
139 local mt
= getmetatable(_M
) or {}
140 mt
.__call
= function(self
, ...)