Module:Jf-JSON: Difference between revisions

Content deleted Content added
upgrade to latest version
update from http://regex.info/code/JSON.lua - adds feature for dealing with trailing garbage
 
(3 intermediate revisions by the same user not shown)
Line 10:
-- http://creativecommons.org/licenses/by/3.0/deed.en_US
--
-- It can be used for any purpose so long as:
-- 1) the copyright notice above, is maintained
-- 2) the web-page links above, andare the 'AUTHOR_NOTE' string below aremaintained
-- 3) the 'AUTHOR_NOTE' string below is maintained
-- maintained. Enjoy.
--
local VERSION = 20160916'20161109.1921' -- version history at end of file
local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 2016091620161109.1921 ]-"
 
--
Line 128 ⟶ 129:
--
--
-- If the JSON text passed to decode() has trailing garbage (e.g. as with the JSON "[123]xyzzy"),
-- the method
--
-- JSON:onTrailingGarbage(json_text, ___location, parsed_value, etc)
--
-- is invoked, where:
--
-- json_text is the original JSON text being parsed,
-- ___location is the count of bytes into json_text where the garbage starts (6 in the example),
-- parsed_value is the Lua result of what was successfully parsed ({123} in the example),
-- etc is as above.
--
-- If JSON:onTrailingGarbage() does not abort, it should return the value decode() should return,
-- or nil + an error message.
--
-- local new_value, error_message = JSON:onTrailingGarbage()
--
-- The default handler just invokes JSON:onDecodeError("trailing garbage"...), but you can have
-- this package ignore trailing garbage via
--
-- function JSON:onTrailingGarbage(json_text, ___location, parsed_value, etc)
-- return parsed_value
-- end
--
--
Line 490 ⟶ 514:
-- onDecodeOfNilError
-- onDecodeOfHTMLError
-- onTrailingGarbage
-- onEncodeError
--
Line 645 ⟶ 670:
if text then
if ___location then
message = string.format("%s at charbyte %d of: %s", message, ___location, text)
else
message = string.format("%s: %s", message, text)
Line 660 ⟶ 685:
assert(false, message)
end
end
 
function OBJDEF:onTrailingGarbage(json_text, ___location, parsed_value, etc)
return self:onDecodeError("trailing garbage", json_text, ___location, etc)
end
 
Line 686 ⟶ 715:
if not integer_part then
self:onDecodeError("expected number", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 735 ⟶ 765:
if not as_number then
self:onDecodeError("bad number", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 745 ⟶ 776:
if text:sub(start,start) ~= '"' then
self:onDecodeError("expected string's opening quote", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 803 ⟶ 835:
 
self:onDecodeError("unclosed string", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 821 ⟶ 854:
if text:sub(start,start) ~= '{' then
self:onDecodeError("expected '{'", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 838 ⟶ 872:
if text:sub(i, i) ~= ':' then
self:onDecodeError("expected colon", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
 
Line 859 ⟶ 894:
if text:sub(i, i) ~= ',' then
self:onDecodeError("expected comma or '}'", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
 
Line 865 ⟶ 901:
 
self:onDecodeError("unclosed '{'", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 870 ⟶ 907:
if text:sub(start,start) ~= '[' then
self:onDecodeError("expected '['", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 898 ⟶ 936:
end
if text:sub(i, i) ~= ',' then
self:onDecodeError("expected comma or '[]'", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
i = skip_whitespace(text, i + 1)
end
self:onDecodeError("unclosed '['", text, start, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
 
Line 912 ⟶ 952:
if start > text:len() then
self:onDecodeError("unexpected end of string", text, nil, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
 
Line 937 ⟶ 978:
else
self:onDecodeError("can't parse JSON", text, start, options.etc)
return nil, 1 -- in case the error method doesn't abort, return something sensible
end
end
Line 958 ⟶ 1,000:
 
if type(self) ~= 'table' or self.__index ~= OBJDEF then
OBJDEF:onDecodeError(local error_message = "JSON:decode must be called in method format", nil, nil, options.etc)
OBJDEF:onDecodeError(error_message, nil, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
 
if text == nil then
self:onDecodeOfNilError(string.format(local error_message = "nil passed to JSON:decode()"), nil, nil, options.etc)
self:onDecodeOfNilError(error_message, nil, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
 
elseif type(text) ~= 'string' then
self:onDecodeError(string.format(local error_message = "expected string argument to JSON:decode(), got %s", type(text)), nil, nil, options.etc)
self:onDecodeError(string.format("%s, got %s", error_message, type(text)), nil, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
 
if text:match('^%s*$') then
-- an empty string is nothing, but not an error
return nil
end
Line 973 ⟶ 1,023:
if text:match('^%s*<') then
-- Can't be JSON... we'll assume it's HTML
self:onDecodeOfHTMLError(string.format(local error_message = "htmlHTML passed to JSON:decode()"), text, nil, options.etc)
self:onDecodeOfHTMLError(error_message, text, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
 
Line 982 ⟶ 1,034:
--
if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then
self:onDecodeError(local error_message = "JSON package groks only UTF-8, sorry", text, nil, options.etc)
self:onDecodeError(error_message, text, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
end
 
Line 998 ⟶ 1,052:
end
 
--
local success, value = pcall(grok_one, self, text, 1, options)
-- Finally, go parse it
--
local success, value, next_i = pcall(grok_one, self, text, 1, options)
 
if success then
 
return value
local error_message = nil
if next_i ~= #text + 1 then
-- something's left over after we parsed the first thing.... whitespace is allowed.
next_i = skip_whitespace(text, next_i)
 
-- if we have something left over now, it's trailing garbage
if next_i ~= #text + 1 then
value, error_message = self:onTrailingGarbage(text, next_i, value, options.etc)
end
end
return value, error_message
 
else
 
-- 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.
-- 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.
local error_message = value
if self.assert then
self.assert(false, valueerror_message)
else
assert(false, valueerror_message)
end
-- ...and if we're still here (because the assert didn't throw an error),
-- return a nil and throw the error message on as a second arg
return nil, valueerror_message
 
end
end
Line 1,297 ⟶ 1,371:
end
 
local function top_level_encode(self, value, etc, options)
local val = encode_value(self, value, {}, etc, options)
if val == nil then
--PRIVATE("may need to revert to the previous public verison if I can't figure out what the guy wanted")
return val
else
return val
end
end
 
function OBJDEF:encode(value, etc, options)
Line 1,310 ⟶ 1,393:
end
 
return encode_valuetop_level_encode(self, value, {}, etc, options)
end
 
Line 1,325 ⟶ 1,408:
end
 
return encode_valuetop_level_encode(self, value, {}, etc, options)
end
 
Line 1,351 ⟶ 1,434:
-- Version history:
--
-- 20161109.21 Oops, had a small boo-boo in the previous update.
--
-- 20161103.20 Used to silently ignore trailing garbage when decoding. Now fails via JSON:onTrailingGarbage()
-- http://seriot.ch/parsing_json.php
--
-- Built-in error message about "expected comma or ']'" had mistakenly referred to '['
--
-- Updated the built-in error reporting to refer to bytes rather than characters.
--
-- The decode() method no longer assumes that error handlers abort.
--
-- Made the VERSION string a string instead of a number
--
 
-- 20160916.19 Fixed the isNumber.__index assignment (thanks to Jack Taylor)
--