1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
3 -- Licensed to the public under the Apache License 2.0.
5 local nixio = require "nixio"
6 local util = require "luci.util"
7 local table = require "table"
8 local string = require "string"
9 local coroutine = require "coroutine"
12 local tonumber = tonumber
13 local tostring = tostring
21 local band = nixio.bit.band
22 local bor = nixio.bit.bor
23 local rshift = nixio.bit.rshift
24 local char = string.char
26 local getmetatable = getmetatable
33 --- Directly decode a JSON string
34 -- @param json JSON-String
36 function decode(json, ...)
37 local a = ActiveDecoder(function() return nil end, ...)
39 local s, obj = pcall(a.get, a)
40 return s and obj or nil
44 --- Direcly encode a Lua object into a JSON string.
45 -- @param obj Lua Object
46 -- @return JSON string
47 function encode(obj, ...)
49 local e = Encoder(obj, 1, ...):source()
55 return not err and table.concat(out) or nil
59 --- Null replacement function
65 --- Create a new JSON-Encoder.
68 -- @param data Lua-Object to be encoded.
69 -- @param buffersize Blocksize of returned data source.
70 -- @param fastescape Use non-standard escaping (don't escape control chars)
71 -- @return JSON-Encoder
72 Encoder = util.class()
74 function Encoder.__init__(self, data, buffersize, fastescape)
76 self.buffersize = buffersize or 512
78 self.fastescape = fastescape
80 getmetatable(self).__call = Encoder.source
83 --- Create an LTN12 source providing the encoded JSON-Data.
84 -- @return LTN12 source
85 function Encoder.source(self)
86 local source = coroutine.create(self.dispatch)
88 local res, data = coroutine.resume(source, self, self.data, true)
97 function Encoder.dispatch(self, data, start)
98 local parser = self.parsers[type(data)]
103 if #self.buffer > 0 then
104 coroutine.yield(self.buffer)
111 function Encoder.put(self, chunk)
112 if self.buffersize < 2 then
113 coroutine.yield(chunk)
115 if #self.buffer + #chunk > self.buffersize then
117 local fbuffer = self.buffersize - #self.buffer
119 coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
122 while #chunk - written > self.buffersize do
123 fbuffer = written + self.buffersize
124 coroutine.yield(chunk:sub(written + 1, fbuffer))
128 self.buffer = chunk:sub(written + 1)
130 self.buffer = self.buffer .. chunk
135 function Encoder.parse_nil(self)
139 function Encoder.parse_bool(self, obj)
140 self:put(obj and "true" or "false")
143 function Encoder.parse_number(self, obj)
144 self:put(tostring(obj))
147 function Encoder.parse_string(self, obj)
148 if self.fastescape then
149 self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
154 return '\\u00%02x' % char:byte()
161 function Encoder.parse_iter(self, obj)
163 return self:put("null")
166 if type(obj) == "table" and (#obj == 0 and next(obj)) then
170 for key, entry in pairs(obj) do
171 first = first or self:put(",")
172 first = first and false
173 self:parse_string(tostring(key))
183 if type(obj) == "table" then
185 first = first or self:put(",")
186 first = first and nil
187 self:dispatch(obj[i])
191 first = first or self:put(",")
192 first = first and nil
202 ['nil'] = Encoder.parse_nil,
203 ['table'] = Encoder.parse_iter,
204 ['number'] = Encoder.parse_number,
205 ['string'] = Encoder.parse_string,
206 ['boolean'] = Encoder.parse_bool,
207 ['function'] = Encoder.parse_iter
211 --- Create a new JSON-Decoder.
214 -- @param customnull Use luci.json.null instead of nil for decoding null
215 -- @return JSON-Decoder
216 Decoder = util.class()
218 function Decoder.__init__(self, customnull)
219 self.cnull = customnull
220 getmetatable(self).__call = Decoder.sink
223 --- Create an LTN12 sink from the decoder object which accepts the JSON-Data.
224 -- @return LTN12 sink
225 function Decoder.sink(self)
226 local sink = coroutine.create(self.dispatch)
228 return coroutine.resume(sink, self, ...)
233 --- Get the decoded data packets after the rawdata has been sent to the sink.
234 -- @return Decoded data
235 function Decoder.get(self)
239 function Decoder.dispatch(self, chunk, src_err, strict)
240 local robject, object
244 while chunk and #chunk < 1 do
248 assert(not strict or chunk, "Unexpected EOS")
249 if not chunk then break end
251 local char = chunk:sub(1, 1)
252 local parser = self.parsers[char]
253 or (char:match("%s") and self.parse_space)
254 or (char:match("[0-9-]") and self.parse_number)
255 or error("Unexpected char '%s'" % char)
257 chunk, robject = parser(self, chunk)
259 if parser ~= self.parse_space then
260 assert(not oset, "Scope violation: Too many objects")
270 assert(not src_err, src_err)
271 assert(oset, "Unexpected EOS")
277 function Decoder.fetch(self)
278 local tself, chunk, src_err = coroutine.yield()
279 assert(chunk or not src_err, src_err)
284 function Decoder.fetch_atleast(self, chunk, bytes)
285 while #chunk < bytes do
286 local nchunk = self:fetch()
287 assert(nchunk, "Unexpected EOS")
288 chunk = chunk .. nchunk
295 function Decoder.fetch_until(self, chunk, pattern)
296 local start = chunk:find(pattern)
299 local nchunk = self:fetch()
300 assert(nchunk, "Unexpected EOS")
301 chunk = chunk .. nchunk
302 start = chunk:find(pattern)
309 function Decoder.parse_space(self, chunk)
310 local start = chunk:find("[^%s]")
317 start = chunk:find("[^%s]")
320 return chunk:sub(start)
324 function Decoder.parse_literal(self, chunk, literal, value)
325 chunk = self:fetch_atleast(chunk, #literal)
326 assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
327 return chunk:sub(#literal + 1), value
331 function Decoder.parse_null(self, chunk)
332 return self:parse_literal(chunk, "null", self.cnull and null)
336 function Decoder.parse_true(self, chunk)
337 return self:parse_literal(chunk, "true", true)
341 function Decoder.parse_false(self, chunk)
342 return self:parse_literal(chunk, "false", false)
346 function Decoder.parse_number(self, chunk)
347 local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
348 local number = tonumber(chunk:sub(1, start - 1))
349 assert(number, "Invalid number specification")
350 return chunk:sub(start), number
354 function Decoder.parse_string(self, chunk)
357 assert(chunk:sub(1, 1) == '"', 'Expected "')
361 local spos = chunk:find('[\\"]')
363 str = str .. chunk:sub(1, spos - 1)
365 local char = chunk:sub(spos, spos)
366 if char == '"' then -- String end
367 chunk = chunk:sub(spos + 1)
369 elseif char == "\\" then -- Escape sequence
370 chunk, object = self:parse_escape(chunk:sub(spos))
376 assert(chunk, "Unexpected EOS while parsing a string")
384 function Decoder.utf8_encode(self, s1, s2)
385 local n = s1 * 256 + s2
387 if n >= 0 and n <= 0x7F then
389 elseif n >= 0 and n <= 0x7FF then
391 bor(band(rshift(n, 6), 0x1F), 0xC0),
392 bor(band(n, 0x3F), 0x80)
394 elseif n >= 0 and n <= 0xFFFF then
396 bor(band(rshift(n, 12), 0x0F), 0xE0),
397 bor(band(rshift(n, 6), 0x3F), 0x80),
398 bor(band(n, 0x3F), 0x80)
400 elseif n >= 0 and n <= 0x10FFFF then
402 bor(band(rshift(n, 18), 0x07), 0xF0),
403 bor(band(rshift(n, 12), 0x3F), 0x80),
404 bor(band(rshift(n, 6), 0x3F), 0x80),
405 bor(band(n, 0x3F), 0x80)
413 function Decoder.parse_escape(self, chunk)
415 chunk = self:fetch_atleast(chunk:sub(2), 1)
416 local char = chunk:sub(1, 1)
421 elseif char == "\\" then
423 elseif char == "u" then
424 chunk = self:fetch_atleast(chunk, 4)
425 local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
426 s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
427 assert(s1 and s2, "Invalid Unicode character")
429 return chunk:sub(5), self:utf8_encode(s1, s2)
430 elseif char == "/" then
432 elseif char == "b" then
434 elseif char == "f" then
436 elseif char == "n" then
438 elseif char == "r" then
440 elseif char == "t" then
443 error("Unexpected escaping sequence '\\%s'" % char)
448 function Decoder.parse_array(self, chunk)
453 local chunk, object = self:parse_delimiter(chunk, "%]")
460 chunk, object = self:dispatch(chunk, nil, true)
461 table.insert(array, nextp, object)
464 chunk, object = self:parse_delimiter(chunk, ",%]")
465 assert(object, "Delimiter expected")
472 function Decoder.parse_object(self, chunk)
477 local chunk, object = self:parse_delimiter(chunk, "}")
484 chunk = self:parse_space(chunk)
485 assert(chunk, "Unexpected EOS")
487 chunk, name = self:parse_string(chunk)
489 chunk, object = self:parse_delimiter(chunk, ":")
490 assert(object, "Separator expected")
492 chunk, object = self:dispatch(chunk, nil, true)
495 chunk, object = self:parse_delimiter(chunk, ",}")
496 assert(object, "Delimiter expected")
503 function Decoder.parse_delimiter(self, chunk, delimiter)
505 chunk = self:fetch_atleast(chunk, 1)
506 local char = chunk:sub(1, 1)
507 if char:match("%s") then
508 chunk = self:parse_space(chunk)
509 assert(chunk, "Unexpected EOS")
510 elseif char:match("[%s]" % delimiter) then
511 return chunk:sub(2), char
520 ['"'] = Decoder.parse_string,
521 ['t'] = Decoder.parse_true,
522 ['f'] = Decoder.parse_false,
523 ['n'] = Decoder.parse_null,
524 ['['] = Decoder.parse_array,
525 ['{'] = Decoder.parse_object
529 --- Create a new Active JSON-Decoder.
531 -- @name ActiveDecoder
532 -- @param customnull Use luci.json.null instead of nil for decoding null
533 -- @return Active JSON-Decoder
534 ActiveDecoder = util.class(Decoder)
536 function ActiveDecoder.__init__(self, source, customnull)
537 Decoder.__init__(self, customnull)
540 getmetatable(self).__call = self.get
544 --- Fetches one JSON-object from given source
545 -- @return Decoded object
546 function ActiveDecoder.get(self)
547 local chunk, src_err, object
548 if not self.chunk then
549 chunk, src_err = self.source()
554 self.chunk, object = self:dispatch(chunk, src_err, true)
559 function ActiveDecoder.fetch(self)
560 local chunk, src_err = self.source()
561 assert(chunk or not src_err, src_err)