· 6 years ago · Jul 16, 2019, 12:29 AM
1-- -*- coding: utf-8 -*-
2--
3-- Simple JSON encoding and decoding in pure Lua.
4--
5-- Copyright 2010-2014 Jeffrey Friedl
6-- http://regex.info/blog/
7--
8-- Latest version: http://regex.info/blog/lua/json
9--
10-- This code is released under a Creative Commons CC-BY "Attribution" License:
11-- http://creativecommons.org/licenses/by/3.0/deed.en_US
12--
13-- It can be used for any purpose so long as the copyright notice above,
14-- the web-page links above, and the 'AUTHOR_NOTE' string below are
15-- maintained. Enjoy.
16--
17local VERSION = 20141223.14 -- version history at end of file
18local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20141223.14 ]-"
19
20--
21-- The 'AUTHOR_NOTE' variable exists so that information about the source
22-- of the package is maintained even in compiled versions. It's also
23-- included in OBJDEF below mostly to quiet warnings about unused variables.
24--
25local OBJDEF = {
26 VERSION = VERSION,
27 AUTHOR_NOTE = AUTHOR_NOTE,
28}
29
30local default_pretty_indent = " "
31local default_pretty_options = { pretty = true, align_keys = false, indent = default_pretty_indent }
32
33local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray
34local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject
35
36
37function OBJDEF:newArray(tbl)
38 return setmetatable(tbl or {}, isArray)
39end
40
41function OBJDEF:newObject(tbl)
42 return setmetatable(tbl or {}, isObject)
43end
44
45local function unicode_codepoint_as_utf8(codepoint)
46 --
47 -- codepoint is a number
48 --
49 if codepoint <= 127 then
50 return string.char(codepoint)
51
52 elseif codepoint <= 2047 then
53 --
54 -- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8
55 --
56 local highpart = math.floor(codepoint / 0x40)
57 local lowpart = codepoint - (0x40 * highpart)
58 return string.char(0xC0 + highpart,
59 0x80 + lowpart)
60
61 elseif codepoint <= 65535 then
62 --
63 -- 1110yyyy 10yyyyxx 10xxxxxx
64 --
65 local highpart = math.floor(codepoint / 0x1000)
66 local remainder = codepoint - 0x1000 * highpart
67 local midpart = math.floor(remainder / 0x40)
68 local lowpart = remainder - 0x40 * midpart
69
70 highpart = 0xE0 + highpart
71 midpart = 0x80 + midpart
72 lowpart = 0x80 + lowpart
73
74 --
75 -- Check for an invalid character (thanks Andy R. at Adobe).
76 -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070
77 --
78 if ( highpart == 0xE0 and midpart < 0xA0 ) or
79 ( highpart == 0xED and midpart > 0x9F ) or
80 ( highpart == 0xF0 and midpart < 0x90 ) or
81 ( highpart == 0xF4 and midpart > 0x8F )
82 then
83 return "?"
84 else
85 return string.char(highpart,
86 midpart,
87 lowpart)
88 end
89
90 else
91 --
92 -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx
93 --
94 local highpart = math.floor(codepoint / 0x40000)
95 local remainder = codepoint - 0x40000 * highpart
96 local midA = math.floor(remainder / 0x1000)
97 remainder = remainder - 0x1000 * midA
98 local midB = math.floor(remainder / 0x40)
99 local lowpart = remainder - 0x40 * midB
100
101 return string.char(0xF0 + highpart,
102 0x80 + midA,
103 0x80 + midB,
104 0x80 + lowpart)
105 end
106end
107
108function OBJDEF:onDecodeError(message, text, location, etc)
109 if text then
110 if location then
111 message = string.format("%s at char %d of: %s", message, location, text)
112 else
113 message = string.format("%s: %s", message, text)
114 end
115 end
116
117 if etc ~= nil then
118 message = message .. " (" .. OBJDEF:encode(etc) .. ")"
119 end
120
121 if self.assert then
122 self.assert(false, message)
123 else
124 assert(false, message)
125 end
126end
127
128OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError
129OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError
130
131function OBJDEF:onEncodeError(message, etc)
132 if etc ~= nil then
133 message = message .. " (" .. OBJDEF:encode(etc) .. ")"
134 end
135
136 if self.assert then
137 self.assert(false, message)
138 else
139 assert(false, message)
140 end
141end
142
143local function grok_number(self, text, start, etc)
144 --
145 -- Grab the integer part
146 --
147 local integer_part = text:match('^-?[1-9]%d*', start)
148 or text:match("^-?0", start)
149
150 if not integer_part then
151 self:onDecodeError("expected number", text, start, etc)
152 end
153
154 local i = start + integer_part:len()
155
156 --
157 -- Grab an optional decimal part
158 --
159 local decimal_part = text:match('^%.%d+', i) or ""
160
161 i = i + decimal_part:len()
162
163 --
164 -- Grab an optional exponential part
165 --
166 local exponent_part = text:match('^[eE][-+]?%d+', i) or ""
167
168 i = i + exponent_part:len()
169
170 local full_number_text = integer_part .. decimal_part .. exponent_part
171 local as_number = tonumber(full_number_text)
172
173 if not as_number then
174 self:onDecodeError("bad number", text, start, etc)
175 end
176
177 return as_number, i
178end
179
180
181local function grok_string(self, text, start, etc)
182
183 if text:sub(start,start) ~= '"' then
184 self:onDecodeError("expected string's opening quote", text, start, etc)
185 end
186
187 local i = start + 1 -- +1 to bypass the initial quote
188 local text_len = text:len()
189 local VALUE = ""
190 while i <= text_len do
191 local c = text:sub(i,i)
192 if c == '"' then
193 return VALUE, i + 1
194 end
195 if c ~= '\\' then
196 VALUE = VALUE .. c
197 i = i + 1
198 elseif text:match('^\\b', i) then
199 VALUE = VALUE .. "\b"
200 i = i + 2
201 elseif text:match('^\\f', i) then
202 VALUE = VALUE .. "\f"
203 i = i + 2
204 elseif text:match('^\\n', i) then
205 VALUE = VALUE .. "\n"
206 i = i + 2
207 elseif text:match('^\\r', i) then
208 VALUE = VALUE .. "\r"
209 i = i + 2
210 elseif text:match('^\\t', i) then
211 VALUE = VALUE .. "\t"
212 i = i + 2
213 else
214 local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i)
215 if hex then
216 i = i + 6 -- bypass what we just read
217
218 -- We have a Unicode codepoint. It could be standalone, or if in the proper range and
219 -- followed by another in a specific range, it'll be a two-code surrogate pair.
220 local codepoint = tonumber(hex, 16)
221 if codepoint >= 0xD800 and codepoint <= 0xDBFF then
222 -- it's a hi surrogate... see whether we have a following low
223 local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i)
224 if lo_surrogate then
225 i = i + 6 -- bypass the low surrogate we just read
226 codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16)
227 else
228 -- not a proper low, so we'll just leave the first codepoint as is and spit it out.
229 end
230 end
231 VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint)
232
233 else
234
235 -- just pass through what's escaped
236 VALUE = VALUE .. text:match('^\\(.)', i)
237 i = i + 2
238 end
239 end
240 end
241
242 self:onDecodeError("unclosed string", text, start, etc)
243end
244
245local function skip_whitespace(text, start)
246
247 local _, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2
248 if match_end then
249 return match_end + 1
250 else
251 return start
252 end
253end
254
255local grok_one -- assigned later
256
257local function grok_object(self, text, start, etc)
258 if text:sub(start,start) ~= '{' then
259 self:onDecodeError("expected '{'", text, start, etc)
260 end
261
262 local i = skip_whitespace(text, start + 1) -- +1 to skip the '{'
263
264 local VALUE = self.strictTypes and self:newObject { } or { }
265
266 if text:sub(i,i) == '}' then
267 return VALUE, i + 1
268 end
269 local text_len = text:len()
270 while i <= text_len do
271 local key, new_i = grok_string(self, text, i, etc)
272
273 i = skip_whitespace(text, new_i)
274
275 if text:sub(i, i) ~= ':' then
276 self:onDecodeError("expected colon", text, i, etc)
277 end
278
279 i = skip_whitespace(text, i + 1)
280
281 local new_val, new_i = grok_one(self, text, i)
282
283 VALUE[key] = new_val
284
285 --
286 -- Expect now either '}' to end things, or a ',' to allow us to continue.
287 --
288 i = skip_whitespace(text, new_i)
289
290 local c = text:sub(i,i)
291
292 if c == '}' then
293 return VALUE, i + 1
294 end
295
296 if text:sub(i, i) ~= ',' then
297 self:onDecodeError("expected comma or '}'", text, i, etc)
298 end
299
300 i = skip_whitespace(text, i + 1)
301 end
302
303 self:onDecodeError("unclosed '{'", text, start, etc)
304end
305
306local function grok_array(self, text, start, etc)
307 if text:sub(start,start) ~= '[' then
308 self:onDecodeError("expected '['", text, start, etc)
309 end
310
311 local i = skip_whitespace(text, start + 1) -- +1 to skip the '['
312 local VALUE = self.strictTypes and self:newArray { } or { }
313 if text:sub(i,i) == ']' then
314 return VALUE, i + 1
315 end
316
317 local VALUE_INDEX = 1
318
319 local text_len = text:len()
320 while i <= text_len do
321 local val, new_i = grok_one(self, text, i)
322
323 -- can't table.insert(VALUE, val) here because it's a no-op if val is nil
324 VALUE[VALUE_INDEX] = val
325 VALUE_INDEX = VALUE_INDEX + 1
326
327 i = skip_whitespace(text, new_i)
328
329 --
330 -- Expect now either ']' to end things, or a ',' to allow us to continue.
331 --
332 local c = text:sub(i,i)
333 if c == ']' then
334 return VALUE, i + 1
335 end
336 if text:sub(i, i) ~= ',' then
337 self:onDecodeError("expected comma or '['", text, i, etc)
338 end
339 i = skip_whitespace(text, i + 1)
340 end
341 self:onDecodeError("unclosed '['", text, start, etc)
342end
343
344
345grok_one = function(self, text, start, etc)
346 -- Skip any whitespace
347 start = skip_whitespace(text, start)
348
349 if start > text:len() then
350 self:onDecodeError("unexpected end of string", text, nil, etc)
351 end
352
353 if text:find('^"', start) then
354 return grok_string(self, text, start, etc)
355
356 elseif text:find('^[-0123456789 ]', start) then
357 return grok_number(self, text, start, etc)
358
359 elseif text:find('^%{', start) then
360 return grok_object(self, text, start, etc)
361
362 elseif text:find('^%[', start) then
363 return grok_array(self, text, start, etc)
364
365 elseif text:find('^true', start) then
366 return true, start + 4
367
368 elseif text:find('^false', start) then
369 return false, start + 5
370
371 elseif text:find('^null', start) then
372 return nil, start + 4
373
374 else
375 self:onDecodeError("can't parse JSON", text, start, etc)
376 end
377end
378
379function OBJDEF:decode(text, etc)
380 if type(self) ~= 'table' or self.__index ~= OBJDEF then
381 OBJDEF:onDecodeError("JSON:decode must be called in method format", nil, nil, etc)
382 end
383
384 if text == nil then
385 self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"), nil, nil, etc)
386 elseif type(text) ~= 'string' then
387 self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s", type(text)), nil, nil, etc)
388 end
389
390 if text:match('^%s*$') then
391 return nil
392 end
393
394 if text:match('^%s*<') then
395 -- Can't be JSON... we'll assume it's HTML
396 self:onDecodeOfHTMLError(string.format("html passed to JSON:decode()"), text, nil, etc)
397 end
398
399 --
400 -- Ensure that it's not UTF-32 or UTF-16.
401 -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3),
402 -- but this package can't handle them.
403 --
404 if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then
405 self:onDecodeError("JSON package groks only UTF-8, sorry", text, nil, etc)
406 end
407
408 local success, value = pcall(grok_one, self, text, 1, etc)
409
410 if success then
411 return value
412 else
413 -- if JSON:onDecodeError() didn't abort out of the pcall, we'll have received the error message here as "value", so pass it along as an assert.
414 if self.assert then
415 self.assert(false, value)
416 else
417 assert(false, value)
418 end
419 -- and if we're still here, return a nil and throw the error message on as a second arg
420 return nil, value
421 end
422end
423
424local function backslash_replacement_function(c)
425 if c == "\n" then
426 return "\\n"
427 elseif c == "\r" then
428 return "\\r"
429 elseif c == "\t" then
430 return "\\t"
431 elseif c == "\b" then
432 return "\\b"
433 elseif c == "\f" then
434 return "\\f"
435 elseif c == '"' then
436 return '\\"'
437 elseif c == '\\' then
438 return '\\\\'
439 else
440 return string.format("\\u%04x", c:byte())
441 end
442end
443
444local chars_to_be_escaped_in_JSON_string
445 = '['
446 .. '"' -- class sub-pattern to match a double quote
447 .. '%\\' -- class sub-pattern to match a backslash
448 .. '%z' -- class sub-pattern to match a null
449 .. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters
450 .. ']'
451
452local function json_string_literal(value)
453 local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function)
454 return '"' .. newval .. '"'
455end
456
457local function object_or_array(self, T, etc)
458 --
459 -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON
460 -- object. If there are only numbers, it's a JSON array.
461 --
462 -- If we'll be converting to a JSON object, we'll want to sort the keys so that the
463 -- end result is deterministic.
464 --
465 local string_keys = { }
466 local number_keys = { }
467 local number_keys_must_be_strings = false
468 local maximum_number_key
469
470 for key in pairs(T) do
471 if type(key) == 'string' then
472 table.insert(string_keys, key)
473 elseif type(key) == 'number' then
474 table.insert(number_keys, key)
475 if key <= 0 or key >= math.huge then
476 number_keys_must_be_strings = true
477 elseif not maximum_number_key or key > maximum_number_key then
478 maximum_number_key = key
479 end
480 else
481 self:onEncodeError("can't encode table with a key of type " .. type(key), etc)
482 end
483 end
484
485 if #string_keys == 0 and not number_keys_must_be_strings then
486 --
487 -- An empty table, or a numeric-only array
488 --
489 if #number_keys > 0 then
490 return nil, maximum_number_key -- an array
491 elseif tostring(T) == "JSON array" then
492 return nil
493 elseif tostring(T) == "JSON object" then
494 return { }
495 else
496 -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects
497 return nil
498 end
499 end
500
501 table.sort(string_keys)
502
503 local map
504 if #number_keys > 0 then
505 --
506 -- If we're here then we have either mixed string/number keys, or numbers inappropriate for a JSON array
507 -- It's not ideal, but we'll turn the numbers into strings so that we can at least create a JSON object.
508 --
509
510 if self.noKeyConversion then
511 self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc)
512 end
513
514 --
515 -- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings
516 --
517 map = { }
518 for key, val in pairs(T) do
519 map[key] = val
520 end
521
522 table.sort(number_keys)
523
524 --
525 -- Throw numeric keys in there as strings
526 --
527 for _, number_key in ipairs(number_keys) do
528 local string_key = tostring(number_key)
529 if map[string_key] == nil then
530 table.insert(string_keys , string_key)
531 map[string_key] = T[number_key]
532 else
533 self:onEncodeError("conflict converting table with mixed-type keys into a JSON object: key " .. number_key .. " exists both as a string and a number.", etc)
534 end
535 end
536 end
537
538 return string_keys, nil, map
539end
540
541--
542-- Encode
543--
544-- 'options' is nil, or a table with possible keys:
545-- pretty -- if true, return a pretty-printed version
546-- indent -- a string (usually of spaces) used to indent each nested level
547-- align_keys -- if true, align all the keys when formatting a table
548--
549local encode_value -- must predeclare because it calls itself
550function encode_value(self, value, parents, etc, options, indent)
551
552 if value == nil then
553 return 'null'
554
555 elseif type(value) == 'string' then
556 return json_string_literal(value)
557
558 elseif type(value) == 'number' then
559 if value ~= value then
560 --
561 -- NaN (Not a Number).
562 -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option.
563 --
564 return "null"
565 elseif value >= math.huge then
566 --
567 -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should
568 -- really be a package option. Note: at least with some implementations, positive infinity
569 -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is.
570 -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">="
571 -- case first.
572 --
573 return "1e+9999"
574 elseif value <= -math.huge then
575 --
576 -- Negative infinity.
577 -- JSON has no INF, so we have to fudge the best we can. This should really be a package option.
578 --
579 return "-1e+9999"
580 else
581 return tostring(value)
582 end
583
584 elseif type(value) == 'boolean' then
585 return tostring(value)
586
587 elseif type(value) ~= 'table' then
588 self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc)
589
590 else
591 --
592 -- A table to be converted to either a JSON object or array.
593 --
594 local T = value
595
596 if type(options) ~= 'table' then
597 options = {}
598 end
599 if type(indent) ~= 'string' then
600 indent = ""
601 end
602
603 if parents[T] then
604 self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc)
605 else
606 parents[T] = true
607 end
608
609 local result_value
610
611 local object_keys, maximum_number_key, map = object_or_array(self, T, etc)
612 if maximum_number_key then
613 --
614 -- An array...
615 --
616 local ITEMS = { }
617 for i = 1, maximum_number_key do
618 table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, indent))
619 end
620
621 if options.pretty then
622 result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]"
623 else
624 result_value = "[" .. table.concat(ITEMS, ",") .. "]"
625 end
626
627 elseif object_keys then
628 --
629 -- An object
630 --
631 local TT = map or T
632
633 if options.pretty then
634
635 local KEYS = { }
636 local max_key_length = 0
637 for _, key in ipairs(object_keys) do
638 local encoded = encode_value(self, tostring(key), parents, etc, options, indent)
639 if options.align_keys then
640 max_key_length = math.max(max_key_length, #encoded)
641 end
642 table.insert(KEYS, encoded)
643 end
644 local key_indent = indent .. tostring(options.indent or "")
645 local subtable_indent = key_indent .. string.rep(" ", max_key_length) .. (options.align_keys and " " or "")
646 local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s"
647
648 local COMBINED_PARTS = { }
649 for i, key in ipairs(object_keys) do
650 local encoded_val = encode_value(self, TT[key], parents, etc, options, subtable_indent)
651 table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val))
652 end
653 result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}"
654
655 else
656
657 local PARTS = { }
658 for _, key in ipairs(object_keys) do
659 local encoded_val = encode_value(self, TT[key], parents, etc, options, indent)
660 local encoded_key = encode_value(self, tostring(key), parents, etc, options, indent)
661 table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val))
662 end
663 result_value = "{" .. table.concat(PARTS, ",") .. "}"
664
665 end
666 else
667 --
668 -- An empty array/object... we'll treat it as an array, though it should really be an option
669 --
670 result_value = "[]"
671 end
672
673 parents[T] = false
674 return result_value
675 end
676end
677
678
679function OBJDEF:encode(value, etc, options)
680 if type(self) ~= 'table' or self.__index ~= OBJDEF then
681 OBJDEF:onEncodeError("JSON:encode must be called in method format", etc)
682 end
683 return encode_value(self, value, {}, etc, options or nil)
684end
685
686function OBJDEF:encode_pretty(value, etc, options)
687 if type(self) ~= 'table' or self.__index ~= OBJDEF then
688 OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc)
689 end
690 return encode_value(self, value, {}, etc, options or default_pretty_options)
691end
692
693function OBJDEF.__tostring()
694 return "JSON encode/decode package"
695end
696
697OBJDEF.__index = OBJDEF
698
699function OBJDEF:new(args)
700 local new = { }
701
702 if args then
703 for key, val in pairs(args) do
704 new[key] = val
705 end
706 end
707
708 return setmetatable(new, OBJDEF)
709end
710
711return OBJDEF:new()