libs/json: Completed JSON library
[project/luci.git] / libs / json / luasrc / json.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 Decoder:
16 Info:
17 null will be decoded to luci.json.null if first parameter of Decoder() is true
18
19 Example:
20 decoder = luci.json.Decoder()
21 luci.ltn12.pump.all(luci.ltn12.source.string("decodableJSON"), decoder:sink())
22 luci.util.dumptable(decoder:get())
23
24 Known issues:
25 does not support unicode conversion \uXXYY with XX != 00 will be ignored
26
27
28 Encoder:
29 Info:
30 Accepts numbers, strings, nil, booleans as they are
31 Accepts luci.json.null as replacement for nil
32 Accepts full associative and full numerically indexed tables
33 Mixed tables will loose their associative values during conversion
34 Iterator functions will be encoded as an array of their return values
35 Non-iterator functions will probably corrupt the encoder
36
37 Example:
38 encoder = luci.json.Encoder(encodableData)
39 luci.ltn12.pump.all(encoder:source(), luci.ltn12.sink.file(io.open("someFile", w)))
40 ]]--
41
42 local util = require "luci.util"
43 local table = require "table"
44 local string = require "string"
45 local coroutine = require "coroutine"
46
47 local assert = assert
48 local tonumber = tonumber
49 local tostring = tostring
50 local error = error
51 local type = type
52 local pairs = pairs
53 local ipairs = ipairs
54 local next = next
55
56 local getmetatable = getmetatable
57
58 module "luci.json"
59
60 --- Null replacement function
61 -- @return null
62 function null()
63 return null
64 end
65
66
67 Encoder = util.class()
68
69 --- Creates a new Encoder.
70 -- @param data Data to be encoded.
71 -- @param buffersize Buffersize of returned data.
72 -- @param fastescape Use non-standard escaping (don't escape control chars)
73 function Encoder.__init__(self, data, buffersize, fastescape)
74 self.data = data
75 self.buffersize = buffersize or 512
76 self.buffer = ""
77 self.fastescape = fastescape
78
79 getmetatable(self).__call = Encoder.source
80 end
81
82 --- Create an LTN12 source from the encoder object
83 -- @return LTN12 source
84 function Encoder.source(self)
85 local source = coroutine.create(self.dispatch)
86 return function()
87 local res, data = coroutine.resume(source, self, self.data, true)
88 if res then
89 return data
90 else
91 return nil, data
92 end
93 end
94 end
95
96 function Encoder.dispatch(self, data, start)
97 local parser = self.parsers[type(data)]
98
99 parser(self, data)
100
101 if start then
102 if #self.buffer > 0 then
103 coroutine.yield(self.buffer)
104 end
105
106 coroutine.yield()
107 end
108 end
109
110 function Encoder.put(self, chunk)
111 if self.buffersize < 2 then
112 corountine.yield(chunk)
113 else
114 if #self.buffer + #chunk > self.buffersize then
115 local written = 0
116 local fbuffer = self.buffersize - #self.buffer
117
118 coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
119 written = fbuffer
120
121 while #chunk - written > self.buffersize do
122 fbuffer = written + self.buffersize
123 coroutine.yield(chunk:sub(written + 1, fbuffer))
124 written = fbuffer
125 end
126
127 self.buffer = chunk:sub(written + 1)
128 else
129 self.buffer = self.buffer .. chunk
130 end
131 end
132 end
133
134 function Encoder.parse_nil(self)
135 self:put("null")
136 end
137
138 function Encoder.parse_bool(self, obj)
139 self:put(obj and "true" or "false")
140 end
141
142 function Encoder.parse_number(self, obj)
143 self:put(tostring(obj))
144 end
145
146 function Encoder.parse_string(self, obj)
147 if self.fastescape then
148 self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
149 else
150 self:put('"' ..
151 obj:gsub('[%c\\"]',
152 function(char)
153 return '\\u00%02x' % char:byte()
154 end
155 )
156 .. '"')
157 end
158 end
159
160 function Encoder.parse_iter(self, obj)
161 if obj == null then
162 return self:put("null")
163 end
164
165 if type(obj) == "table" and (#obj == 0 and next(obj)) then
166 self:put("{")
167 local first = true
168
169 for key, entry in pairs(obj) do
170 first = first or self:put(",")
171 first = first and false
172 self:parse_string(tostring(key))
173 self:put(":")
174 self:dispatch(entry)
175 end
176
177 self:put("}")
178 else
179 self:put("[")
180 local first = true
181
182 if type(obj) == "table" then
183 for i, entry in pairs(obj) do
184 first = first or self:put(",")
185 first = first and nil
186 self:dispatch(entry)
187 end
188 else
189 for entry in obj do
190 first = first or self:put(",")
191 first = first and nil
192 self:dispatch(entry)
193 end
194 end
195
196 self:put("]")
197 end
198 end
199
200 Encoder.parsers = {
201 ['nil'] = Encoder.parse_nil,
202 ['table'] = Encoder.parse_iter,
203 ['number'] = Encoder.parse_number,
204 ['string'] = Encoder.parse_string,
205 ['boolean'] = Encoder.parse_bool,
206 ['function'] = Encoder.parse_iter
207 }
208
209
210
211 Decoder = util.class()
212
213 --- Create a new Decoder object.
214 -- @param customnull User luci.json.null instead of nil
215 function Decoder.__init__(self, customnull)
216 self.cnull = customnull
217 getmetatable(self).__call = Decoder.sink
218 end
219
220 --- Create an LTN12 sink from the decoder object.
221 -- @return LTN12 sink
222 function Decoder.sink(self)
223 local sink = coroutine.create(self.dispatch)
224 return function(...)
225 return coroutine.resume(sink, self, ...)
226 end
227 end
228
229
230 --- Get the decoded data packets
231 -- @return Decoded data
232 function Decoder.get(self)
233 return self.data
234 end
235
236 function Decoder.dispatch(self, chunk, src_err, strict)
237 local robject, object
238 local oset = false
239
240 while chunk do
241 while chunk and #chunk < 1 do
242 chunk = self:fetch()
243 end
244
245 assert(not strict or chunk, "Unexpected EOS")
246 if not chunk then break end
247
248 local char = chunk:sub(1, 1)
249 local parser = self.parsers[char]
250 or (char:match("%s") and self.parse_space)
251 or (char:match("[0-9-]") and self.parse_number)
252 or error("Unexpected char '%s'" % char)
253
254 chunk, robject = parser(self, chunk)
255
256 if parser ~= self.parse_space then
257 assert(not oset, "Scope violation: Too many objects")
258 object = robject
259 oset = true
260
261 if strict then
262 return chunk, object
263 end
264 end
265 end
266
267 assert(not src_err, src_err)
268 assert(oset, "Unexpected EOS")
269
270 self.data = object
271 end
272
273
274 function Decoder.fetch(self)
275 local tself, chunk, src_err = coroutine.yield()
276 assert(chunk or not src_err, src_err)
277 return chunk
278 end
279
280
281 function Decoder.fetch_atleast(self, chunk, bytes)
282 while #chunk < bytes do
283 local nchunk = self:fetch()
284 assert(nchunk, "Unexpected EOS")
285 chunk = chunk .. nchunk
286 end
287
288 return chunk
289 end
290
291
292 function Decoder.fetch_until(self, chunk, pattern)
293 local start = chunk:find(pattern)
294
295 while not start do
296 local nchunk = self:fetch()
297 assert(nchunk, "Unexpected EOS")
298 chunk = chunk .. nchunk
299 start = chunk:find(pattern)
300 end
301
302 return chunk, start
303 end
304
305
306 function Decoder.parse_space(self, chunk)
307 local start = chunk:find("[^%s]")
308
309 while not start do
310 chunk = self:fetch()
311 if not chunk then
312 return nil
313 end
314 start = chunk:find("[^%s]")
315 end
316
317 return chunk:sub(start)
318 end
319
320
321 function Decoder.parse_literal(self, chunk, literal, value)
322 chunk = self:fetch_atleast(chunk, #literal)
323 assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
324 return chunk:sub(#literal + 1), value
325 end
326
327
328 function Decoder.parse_null(self, chunk)
329 return self:parse_literal(chunk, "null", self.cnull and null)
330 end
331
332
333 function Decoder.parse_true(self, chunk)
334 return self:parse_literal(chunk, "true", true)
335 end
336
337
338 function Decoder.parse_false(self, chunk)
339 return self:parse_literal(chunk, "false", false)
340 end
341
342
343 function Decoder.parse_number(self, chunk)
344 local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
345 local number = tonumber(chunk:sub(1, start - 1))
346 assert(number, "Invalid number specification")
347 return chunk:sub(start), number
348 end
349
350
351 function Decoder.parse_string(self, chunk)
352 local str = ""
353 local object = nil
354 assert(chunk:sub(1, 1) == '"', 'Expected "')
355 chunk = chunk:sub(2)
356
357 while true do
358 local spos = chunk:find('[\\"]')
359 if spos then
360 str = str .. chunk:sub(1, spos - 1)
361
362 local char = chunk:sub(spos, spos)
363 if char == '"' then -- String end
364 chunk = chunk:sub(spos + 1)
365 break
366 elseif char == "\\" then -- Escape sequence
367 chunk, object = self:parse_escape(chunk:sub(spos))
368 str = str .. object
369 end
370 else
371 str = str .. chunk
372 chunk = self:fetch()
373 assert(chunk, "Unexpected EOS while parsing a string")
374 end
375 end
376
377 return chunk, str
378 end
379
380
381 function Decoder.parse_escape(self, chunk)
382 local str = ""
383 chunk = self:fetch_atleast(chunk:sub(2), 1)
384 local char = chunk:sub(1, 1)
385 chunk = chunk:sub(2)
386
387 if char == '"' then
388 return chunk, '"'
389 elseif char == "\\" then
390 return chunk, "\\"
391 elseif char == "u" then
392 chunk = self:fetch_atleast(chunk, 4)
393 local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
394 s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
395 assert(s1 and s2, "Invalid Unicode character")
396
397 -- ToDo: Unicode support
398 return chunk:sub(5), s1 == 0 and string.char(s2) or ""
399 elseif char == "/" then
400 return chunk, "/"
401 elseif char == "b" then
402 return chunk, "\b"
403 elseif char == "f" then
404 return chunk, "\f"
405 elseif char == "n" then
406 return chunk, "\n"
407 elseif char == "r" then
408 return chunk, "\r"
409 elseif char == "t" then
410 return chunk, "\t"
411 else
412 error("Unexpected escaping sequence '\\%s'" % char)
413 end
414 end
415
416
417 function Decoder.parse_array(self, chunk)
418 chunk = chunk:sub(2)
419 local array = {}
420 local nextp = 1
421
422 local chunk, object = self:parse_delimiter(chunk, "%]")
423
424 if object then
425 return chunk, array
426 end
427
428 repeat
429 chunk, object = self:dispatch(chunk, nil, true)
430 table.insert(array, nextp, object)
431 nextp = nextp + 1
432
433 chunk, object = self:parse_delimiter(chunk, ",%]")
434 assert(object, "Delimiter expected")
435 until object == "]"
436
437 return chunk, array
438 end
439
440
441 function Decoder.parse_object(self, chunk)
442 chunk = chunk:sub(2)
443 local array = {}
444 local name
445
446 local chunk, object = self:parse_delimiter(chunk, "}")
447
448 if object then
449 return chunk, array
450 end
451
452 repeat
453 chunk = self:parse_space(chunk)
454 assert(chunk, "Unexpected EOS")
455
456 chunk, name = self:parse_string(chunk)
457
458 chunk, object = self:parse_delimiter(chunk, ":")
459 assert(object, "Separator expected")
460
461 chunk, object = self:dispatch(chunk, nil, true)
462 array[name] = object
463
464 chunk, object = self:parse_delimiter(chunk, ",}")
465 assert(object, "Delimiter expected")
466 until object == "}"
467
468 return chunk, array
469 end
470
471
472 function Decoder.parse_delimiter(self, chunk, delimiter)
473 while true do
474 chunk = self:fetch_atleast(chunk, 1)
475 local char = chunk:sub(1, 1)
476 if char:match("%s") then
477 chunk = self:parse_space(chunk)
478 assert(chunk, "Unexpected EOS")
479 elseif char:match("[%s]" % delimiter) then
480 return chunk:sub(2), char
481 else
482 return chunk, nil
483 end
484 end
485 end
486
487
488 Decoder.parsers = {
489 ['"'] = Decoder.parse_string,
490 ['t'] = Decoder.parse_true,
491 ['f'] = Decoder.parse_false,
492 ['n'] = Decoder.parse_null,
493 ['['] = Decoder.parse_array,
494 ['{'] = Decoder.parse_object
495 }