<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://bukvica.org/w/index.php?action=history&amp;feed=atom&amp;title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%3AJSON</id>
	<title>Модуль:JSON - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://bukvica.org/w/index.php?action=history&amp;feed=atom&amp;title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%3AJSON"/>
	<link rel="alternate" type="text/html" href="https://bukvica.org/w/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:JSON&amp;action=history"/>
	<updated>2026-06-22T00:32:59Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://bukvica.org/w/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:JSON&amp;diff=30331&amp;oldid=prev</id>
		<title>Karaby: Новая страница: «-- Simple JSON encoding and decoding in pure Lua. -- -- Copyright 2010-2014 Jeffrey Friedl -- http://regex.info/blog/ -- -- Latest version: http://regex.info/blog…»</title>
		<link rel="alternate" type="text/html" href="https://bukvica.org/w/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:JSON&amp;diff=30331&amp;oldid=prev"/>
		<updated>2015-03-06T10:24:37Z</updated>

		<summary type="html">&lt;p&gt;Новая страница: «-- Simple JSON encoding and decoding in pure Lua. -- -- Copyright 2010-2014 Jeffrey Friedl -- http://regex.info/blog/ -- -- Latest version: http://regex.info/blog…»&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Simple JSON encoding and decoding in pure Lua.&lt;br /&gt;
--&lt;br /&gt;
-- Copyright 2010-2014 Jeffrey Friedl&lt;br /&gt;
-- http://regex.info/blog/&lt;br /&gt;
--&lt;br /&gt;
-- Latest version: http://regex.info/blog/lua/json&lt;br /&gt;
--&lt;br /&gt;
-- This code is released under a Creative Commons CC-BY &amp;quot;Attribution&amp;quot; License:&lt;br /&gt;
-- http://creativecommons.org/licenses/by/3.0/deed.en_US&lt;br /&gt;
--&lt;br /&gt;
-- It can be used for any purpose so long as the copyright notice and&lt;br /&gt;
-- web-page links above are maintained. Enjoy.&lt;br /&gt;
--&lt;br /&gt;
local VERSION = 20140911.12  -- version history at end of file&lt;br /&gt;
local OBJDEF = { VERSION = VERSION }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--&lt;br /&gt;
-- Simple JSON encoding and decoding in pure Lua.&lt;br /&gt;
-- http://www.json.org/&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
--   JSON = (loadfile &amp;quot;JSON.lua&amp;quot;)() -- one-time load of the routines&lt;br /&gt;
--&lt;br /&gt;
--   local lua_value = JSON:decode(raw_json_text)&lt;br /&gt;
--&lt;br /&gt;
--   local raw_json_text    = JSON:encode(lua_table_or_value)&lt;br /&gt;
--   local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- &amp;quot;pretty printed&amp;quot; version for human readability&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
-- DECODING&lt;br /&gt;
--&lt;br /&gt;
--   JSON = (loadfile &amp;quot;JSON.lua&amp;quot;)() -- one-time load of the routines&lt;br /&gt;
--&lt;br /&gt;
--   local lua_value = JSON:decode(raw_json_text)&lt;br /&gt;
--&lt;br /&gt;
--   If the JSON text is for an object or an array, e.g.&lt;br /&gt;
--     { &amp;quot;what&amp;quot;: &amp;quot;books&amp;quot;, &amp;quot;count&amp;quot;: 3 }&lt;br /&gt;
--   or&lt;br /&gt;
--     [ &amp;quot;Larry&amp;quot;, &amp;quot;Curly&amp;quot;, &amp;quot;Moe&amp;quot; ]&lt;br /&gt;
--&lt;br /&gt;
--   the result is a Lua table, e.g.&lt;br /&gt;
--     { what = &amp;quot;books&amp;quot;, count = 3 }&lt;br /&gt;
--   or&lt;br /&gt;
--     { &amp;quot;Larry&amp;quot;, &amp;quot;Curly&amp;quot;, &amp;quot;Moe&amp;quot; }&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
--   The encode and decode routines accept an optional second argument, &amp;quot;etc&amp;quot;, which is not used&lt;br /&gt;
--   during encoding or decoding, but upon error is passed along to error handlers. It can be of any&lt;br /&gt;
--   type (including nil).&lt;br /&gt;
--&lt;br /&gt;
--   With most errors during decoding, this code calls&lt;br /&gt;
--&lt;br /&gt;
--      JSON:onDecodeError(message, text, location, etc)&lt;br /&gt;
--&lt;br /&gt;
--   with a message about the error, and if known, the JSON text being parsed and the byte count&lt;br /&gt;
--   where the problem was discovered. You can replace the default JSON:onDecodeError() with your&lt;br /&gt;
--   own function.&lt;br /&gt;
--&lt;br /&gt;
--   The default onDecodeError() merely augments the message with data about the text and the&lt;br /&gt;
--   location if known (and if a second &amp;#039;etc&amp;#039; argument had been provided to decode(), its value is&lt;br /&gt;
--   tacked onto the message as well), and then calls JSON.assert(), which itself defaults to Lua&amp;#039;s&lt;br /&gt;
--   built-in assert(), and can also be overridden.&lt;br /&gt;
--&lt;br /&gt;
--   For example, in an Adobe Lightroom plugin, you might use something like&lt;br /&gt;
--&lt;br /&gt;
--          function JSON:onDecodeError(message, text, location, etc)&lt;br /&gt;
--             LrErrors.throwUserError(&amp;quot;Internal Error: invalid JSON data&amp;quot;)&lt;br /&gt;
--          end&lt;br /&gt;
--&lt;br /&gt;
--   or even just&lt;br /&gt;
--&lt;br /&gt;
--          function JSON.assert(message)&lt;br /&gt;
--             LrErrors.throwUserError(&amp;quot;Internal Error: &amp;quot; .. message)&lt;br /&gt;
--          end&lt;br /&gt;
--&lt;br /&gt;
--   If JSON:decode() is passed a nil, this is called instead:&lt;br /&gt;
--&lt;br /&gt;
--      JSON:onDecodeOfNilError(message, nil, nil, etc)&lt;br /&gt;
--&lt;br /&gt;
--   and if JSON:decode() is passed HTML instead of JSON, this is called:&lt;br /&gt;
--&lt;br /&gt;
--      JSON:onDecodeOfHTMLError(message, text, nil, etc)&lt;br /&gt;
--&lt;br /&gt;
--   The use of the fourth &amp;#039;etc&amp;#039; argument allows stronger coordination between decoding and error&lt;br /&gt;
--   reporting, especially when you provide your own error-handling routines. Continuing with the&lt;br /&gt;
--   the Adobe Lightroom plugin example:&lt;br /&gt;
--&lt;br /&gt;
--          function JSON:onDecodeError(message, text, location, etc)&lt;br /&gt;
--             local note = &amp;quot;Internal Error: invalid JSON data&amp;quot;&lt;br /&gt;
--             if type(etc) = &amp;#039;table&amp;#039; and etc.photo then&lt;br /&gt;
--                note = note .. &amp;quot; while processing for &amp;quot; .. etc.photo:getFormattedMetadata(&amp;#039;fileName&amp;#039;)&lt;br /&gt;
--             end&lt;br /&gt;
--             LrErrors.throwUserError(note)&lt;br /&gt;
--          end&lt;br /&gt;
--&lt;br /&gt;
--            :&lt;br /&gt;
--            :&lt;br /&gt;
--&lt;br /&gt;
--          for i, photo in ipairs(photosToProcess) do&lt;br /&gt;
--               :             &lt;br /&gt;
--               :             &lt;br /&gt;
--               local data = JSON:decode(someJsonText, { photo = photo })&lt;br /&gt;
--               :             &lt;br /&gt;
--               :             &lt;br /&gt;
--          end&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
&lt;br /&gt;
-- DECODING AND STRICT TYPES&lt;br /&gt;
--&lt;br /&gt;
--   Because both JSON objects and JSON arrays are converted to Lua tables, it&amp;#039;s not normally&lt;br /&gt;
--   possible to tell which a JSON type a particular Lua table was derived from, or guarantee&lt;br /&gt;
--   decode-encode round-trip equivalency.&lt;br /&gt;
--&lt;br /&gt;
--   However, if you enable strictTypes, e.g.&lt;br /&gt;
--&lt;br /&gt;
--      JSON = (loadfile &amp;quot;JSON.lua&amp;quot;)() --load the routines&lt;br /&gt;
--      JSON.strictTypes = true&lt;br /&gt;
--&lt;br /&gt;
--   then the Lua table resulting from the decoding of a JSON object or JSON array is marked via Lua&lt;br /&gt;
--   metatable, so that when re-encoded with JSON:encode() it ends up as the appropriate JSON type.&lt;br /&gt;
--&lt;br /&gt;
--   (This is not the default because other routines may not work well with tables that have a&lt;br /&gt;
--   metatable set, for example, Lightroom API calls.)&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
-- ENCODING&lt;br /&gt;
--&lt;br /&gt;
--   JSON = (loadfile &amp;quot;JSON.lua&amp;quot;)() -- one-time load of the routines&lt;br /&gt;
--&lt;br /&gt;
--   local raw_json_text    = JSON:encode(lua_table_or_value)&lt;br /&gt;
--   local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- &amp;quot;pretty printed&amp;quot; version for human readability&lt;br /&gt;
&lt;br /&gt;
--   On error during encoding, this code calls:&lt;br /&gt;
--&lt;br /&gt;
--    JSON:onEncodeError(message, etc)&lt;br /&gt;
--&lt;br /&gt;
--   which you can override in your local JSON object.&lt;br /&gt;
--&lt;br /&gt;
--   If the Lua table contains both string and numeric keys, it fits neither JSON&amp;#039;s&lt;br /&gt;
--   idea of an object, nor its idea of an array. To get around this, when any string&lt;br /&gt;
--   key exists (or when non-positive numeric keys exist), numeric keys are converted&lt;br /&gt;
--   to strings.&lt;br /&gt;
--&lt;br /&gt;
--   For example, &lt;br /&gt;
--     JSON:encode({ &amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, &amp;quot;three&amp;quot;, SOMESTRING = &amp;quot;some string&amp;quot; }))&lt;br /&gt;
--   produces the JSON object&lt;br /&gt;
--     {&amp;quot;1&amp;quot;:&amp;quot;one&amp;quot;,&amp;quot;2&amp;quot;:&amp;quot;two&amp;quot;,&amp;quot;3&amp;quot;:&amp;quot;three&amp;quot;,&amp;quot;SOMESTRING&amp;quot;:&amp;quot;some string&amp;quot;}&lt;br /&gt;
--&lt;br /&gt;
--   To prohibit this conversion and instead make it an error condition, set&lt;br /&gt;
--      JSON.noKeyConversion = true&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--&lt;br /&gt;
-- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT&lt;br /&gt;
--&lt;br /&gt;
--    assert&lt;br /&gt;
--    onDecodeError&lt;br /&gt;
--    onDecodeOfNilError&lt;br /&gt;
--    onDecodeOfHTMLError&lt;br /&gt;
--    onEncodeError&lt;br /&gt;
--&lt;br /&gt;
--  If you want to create a separate Lua JSON object with its own error handlers,&lt;br /&gt;
--  you can reload JSON.lua or use the :new() method.&lt;br /&gt;
--&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local isArray  = { __tostring = function() return &amp;quot;JSON array&amp;quot;  end }    isArray.__index  = isArray&lt;br /&gt;
local isObject = { __tostring = function() return &amp;quot;JSON object&amp;quot; end }    isObject.__index = isObject&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:newArray(tbl)&lt;br /&gt;
   return setmetatable(tbl or {}, isArray)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:newObject(tbl)&lt;br /&gt;
   return setmetatable(tbl or {}, isObject)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function unicode_codepoint_as_utf8(codepoint)&lt;br /&gt;
   --&lt;br /&gt;
   -- codepoint is a number&lt;br /&gt;
   --&lt;br /&gt;
   if codepoint &amp;lt;= 127 then&lt;br /&gt;
      return string.char(codepoint)&lt;br /&gt;
&lt;br /&gt;
   elseif codepoint &amp;lt;= 2047 then&lt;br /&gt;
      --&lt;br /&gt;
      -- 110yyyxx 10xxxxxx         &amp;lt;-- useful notation from http://en.wikipedia.org/wiki/Utf8&lt;br /&gt;
      --&lt;br /&gt;
      local highpart = math.floor(codepoint / 0x40)&lt;br /&gt;
      local lowpart  = codepoint - (0x40 * highpart)&lt;br /&gt;
      return string.char(0xC0 + highpart,&lt;br /&gt;
                         0x80 + lowpart)&lt;br /&gt;
&lt;br /&gt;
   elseif codepoint &amp;lt;= 65535 then&lt;br /&gt;
      --&lt;br /&gt;
      -- 1110yyyy 10yyyyxx 10xxxxxx&lt;br /&gt;
      --&lt;br /&gt;
      local highpart  = math.floor(codepoint / 0x1000)&lt;br /&gt;
      local remainder = codepoint - 0x1000 * highpart&lt;br /&gt;
      local midpart   = math.floor(remainder / 0x40)&lt;br /&gt;
      local lowpart   = remainder - 0x40 * midpart&lt;br /&gt;
&lt;br /&gt;
      highpart = 0xE0 + highpart&lt;br /&gt;
      midpart  = 0x80 + midpart&lt;br /&gt;
      lowpart  = 0x80 + lowpart&lt;br /&gt;
&lt;br /&gt;
      --&lt;br /&gt;
      -- Check for an invalid character (thanks Andy R. at Adobe).&lt;br /&gt;
      -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070&lt;br /&gt;
      --&lt;br /&gt;
      if ( highpart == 0xE0 and midpart &amp;lt; 0xA0 ) or&lt;br /&gt;
         ( highpart == 0xED and midpart &amp;gt; 0x9F ) or&lt;br /&gt;
         ( highpart == 0xF0 and midpart &amp;lt; 0x90 ) or&lt;br /&gt;
         ( highpart == 0xF4 and midpart &amp;gt; 0x8F )&lt;br /&gt;
      then&lt;br /&gt;
         return &amp;quot;?&amp;quot;&lt;br /&gt;
      else&lt;br /&gt;
         return string.char(highpart,&lt;br /&gt;
                            midpart,&lt;br /&gt;
                            lowpart)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
   else&lt;br /&gt;
      --&lt;br /&gt;
      -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx&lt;br /&gt;
      --&lt;br /&gt;
      local highpart  = math.floor(codepoint / 0x40000)&lt;br /&gt;
      local remainder = codepoint - 0x40000 * highpart&lt;br /&gt;
      local midA      = math.floor(remainder / 0x1000)&lt;br /&gt;
      remainder       = remainder - 0x1000 * midA&lt;br /&gt;
      local midB      = math.floor(remainder / 0x40)&lt;br /&gt;
      local lowpart   = remainder - 0x40 * midB&lt;br /&gt;
&lt;br /&gt;
      return string.char(0xF0 + highpart,&lt;br /&gt;
                         0x80 + midA,&lt;br /&gt;
                         0x80 + midB,&lt;br /&gt;
                         0x80 + lowpart)&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:onDecodeError(message, text, location, etc)&lt;br /&gt;
   if text then&lt;br /&gt;
      if location then&lt;br /&gt;
         message = string.format(&amp;quot;%s at char %d of: %s&amp;quot;, message, location, text)&lt;br /&gt;
      else&lt;br /&gt;
         message = string.format(&amp;quot;%s: %s&amp;quot;, message, text)&lt;br /&gt;
      end&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if etc ~= nil then&lt;br /&gt;
      message = message .. &amp;quot; (&amp;quot; .. OBJDEF:encode(etc) .. &amp;quot;)&amp;quot;&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if self.assert then&lt;br /&gt;
      self.assert(false, message)&lt;br /&gt;
   else&lt;br /&gt;
      assert(false, message)&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
OBJDEF.onDecodeOfNilError  = OBJDEF.onDecodeError&lt;br /&gt;
OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:onEncodeError(message, etc)&lt;br /&gt;
   if etc ~= nil then&lt;br /&gt;
      message = message .. &amp;quot; (&amp;quot; .. OBJDEF:encode(etc) .. &amp;quot;)&amp;quot;&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if self.assert then&lt;br /&gt;
      self.assert(false, message)&lt;br /&gt;
   else&lt;br /&gt;
      assert(false, message)&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function grok_number(self, text, start, etc)&lt;br /&gt;
   --&lt;br /&gt;
   -- Grab the integer part&lt;br /&gt;
   --&lt;br /&gt;
   local integer_part = text:match(&amp;#039;^-?[1-9]%d*&amp;#039;, start)&lt;br /&gt;
                     or text:match(&amp;quot;^-?0&amp;quot;,        start)&lt;br /&gt;
&lt;br /&gt;
   if not integer_part then&lt;br /&gt;
      self:onDecodeError(&amp;quot;expected number&amp;quot;, text, start, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   local i = start + integer_part:len()&lt;br /&gt;
&lt;br /&gt;
   --&lt;br /&gt;
   -- Grab an optional decimal part&lt;br /&gt;
   --&lt;br /&gt;
   local decimal_part = text:match(&amp;#039;^%.%d+&amp;#039;, i) or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   i = i + decimal_part:len()&lt;br /&gt;
&lt;br /&gt;
   --&lt;br /&gt;
   -- Grab an optional exponential part&lt;br /&gt;
   --&lt;br /&gt;
   local exponent_part = text:match(&amp;#039;^[eE][-+]?%d+&amp;#039;, i) or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   i = i + exponent_part:len()&lt;br /&gt;
&lt;br /&gt;
   local full_number_text = integer_part .. decimal_part .. exponent_part&lt;br /&gt;
   local as_number = tonumber(full_number_text)&lt;br /&gt;
&lt;br /&gt;
   if not as_number then&lt;br /&gt;
      self:onDecodeError(&amp;quot;bad number&amp;quot;, text, start, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   return as_number, i&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function grok_string(self, text, start, etc)&lt;br /&gt;
&lt;br /&gt;
   if text:sub(start,start) ~= &amp;#039;&amp;quot;&amp;#039; then&lt;br /&gt;
      self:onDecodeError(&amp;quot;expected string&amp;#039;s opening quote&amp;quot;, text, start, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   local i = start + 1 -- +1 to bypass the initial quote&lt;br /&gt;
   local text_len = text:len()&lt;br /&gt;
   local VALUE = &amp;quot;&amp;quot;&lt;br /&gt;
   while i &amp;lt;= text_len do&lt;br /&gt;
      local c = text:sub(i,i)&lt;br /&gt;
      if c == &amp;#039;&amp;quot;&amp;#039; then&lt;br /&gt;
         return VALUE, i + 1&lt;br /&gt;
      end&lt;br /&gt;
      if c ~= &amp;#039;\\&amp;#039; then&lt;br /&gt;
         VALUE = VALUE .. c&lt;br /&gt;
         i = i + 1&lt;br /&gt;
      elseif text:match(&amp;#039;^\\b&amp;#039;, i) then&lt;br /&gt;
         VALUE = VALUE .. &amp;quot;\b&amp;quot;&lt;br /&gt;
         i = i + 2&lt;br /&gt;
      elseif text:match(&amp;#039;^\\f&amp;#039;, i) then&lt;br /&gt;
         VALUE = VALUE .. &amp;quot;\f&amp;quot;&lt;br /&gt;
         i = i + 2&lt;br /&gt;
      elseif text:match(&amp;#039;^\\n&amp;#039;, i) then&lt;br /&gt;
         VALUE = VALUE .. &amp;quot;\n&amp;quot;&lt;br /&gt;
         i = i + 2&lt;br /&gt;
      elseif text:match(&amp;#039;^\\r&amp;#039;, i) then&lt;br /&gt;
         VALUE = VALUE .. &amp;quot;\r&amp;quot;&lt;br /&gt;
         i = i + 2&lt;br /&gt;
      elseif text:match(&amp;#039;^\\t&amp;#039;, i) then&lt;br /&gt;
         VALUE = VALUE .. &amp;quot;\t&amp;quot;&lt;br /&gt;
         i = i + 2&lt;br /&gt;
      else&lt;br /&gt;
         local hex = text:match(&amp;#039;^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])&amp;#039;, i)&lt;br /&gt;
         if hex then&lt;br /&gt;
            i = i + 6 -- bypass what we just read&lt;br /&gt;
&lt;br /&gt;
            -- We have a Unicode codepoint. It could be standalone, or if in the proper range and&lt;br /&gt;
            -- followed by another in a specific range, it&amp;#039;ll be a two-code surrogate pair.&lt;br /&gt;
            local codepoint = tonumber(hex, 16)&lt;br /&gt;
            if codepoint &amp;gt;= 0xD800 and codepoint &amp;lt;= 0xDBFF then&lt;br /&gt;
               -- it&amp;#039;s a hi surrogate... see whether we have a following low&lt;br /&gt;
               local lo_surrogate = text:match(&amp;#039;^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])&amp;#039;, i)&lt;br /&gt;
               if lo_surrogate then&lt;br /&gt;
                  i = i + 6 -- bypass the low surrogate we just read&lt;br /&gt;
                  codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16)&lt;br /&gt;
               else&lt;br /&gt;
                  -- not a proper low, so we&amp;#039;ll just leave the first codepoint as is and spit it out.&lt;br /&gt;
               end&lt;br /&gt;
            end&lt;br /&gt;
            VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint)&lt;br /&gt;
&lt;br /&gt;
         else&lt;br /&gt;
&lt;br /&gt;
            -- just pass through what&amp;#039;s escaped&lt;br /&gt;
            VALUE = VALUE .. text:match(&amp;#039;^\\(.)&amp;#039;, i)&lt;br /&gt;
            i = i + 2&lt;br /&gt;
         end&lt;br /&gt;
      end&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   self:onDecodeError(&amp;quot;unclosed string&amp;quot;, text, start, etc)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function skip_whitespace(text, start)&lt;br /&gt;
&lt;br /&gt;
   local _, match_end = text:find(&amp;quot;^[ \n\r\t]+&amp;quot;, start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2&lt;br /&gt;
   if match_end then&lt;br /&gt;
      return match_end + 1&lt;br /&gt;
   else&lt;br /&gt;
      return start&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local grok_one -- assigned later&lt;br /&gt;
&lt;br /&gt;
local function grok_object(self, text, start, etc)&lt;br /&gt;
   if text:sub(start,start) ~= &amp;#039;{&amp;#039; then&lt;br /&gt;
      self:onDecodeError(&amp;quot;expected &amp;#039;{&amp;#039;&amp;quot;, text, start, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   local i = skip_whitespace(text, start + 1) -- +1 to skip the &amp;#039;{&amp;#039;&lt;br /&gt;
&lt;br /&gt;
   local VALUE = self.strictTypes and self:newObject { } or { }&lt;br /&gt;
&lt;br /&gt;
   if text:sub(i,i) == &amp;#039;}&amp;#039; then&lt;br /&gt;
      return VALUE, i + 1&lt;br /&gt;
   end&lt;br /&gt;
   local text_len = text:len()&lt;br /&gt;
   while i &amp;lt;= text_len do&lt;br /&gt;
      local key, new_i = grok_string(self, text, i, etc)&lt;br /&gt;
&lt;br /&gt;
      i = skip_whitespace(text, new_i)&lt;br /&gt;
&lt;br /&gt;
      if text:sub(i, i) ~= &amp;#039;:&amp;#039; then&lt;br /&gt;
         self:onDecodeError(&amp;quot;expected colon&amp;quot;, text, i, etc)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      i = skip_whitespace(text, i + 1)&lt;br /&gt;
&lt;br /&gt;
      local new_val, new_i = grok_one(self, text, i)&lt;br /&gt;
&lt;br /&gt;
      VALUE[key] = new_val&lt;br /&gt;
&lt;br /&gt;
      --&lt;br /&gt;
      -- Expect now either &amp;#039;}&amp;#039; to end things, or a &amp;#039;,&amp;#039; to allow us to continue.&lt;br /&gt;
      --&lt;br /&gt;
      i = skip_whitespace(text, new_i)&lt;br /&gt;
&lt;br /&gt;
      local c = text:sub(i,i)&lt;br /&gt;
&lt;br /&gt;
      if c == &amp;#039;}&amp;#039; then&lt;br /&gt;
         return VALUE, i + 1&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      if text:sub(i, i) ~= &amp;#039;,&amp;#039; then&lt;br /&gt;
         self:onDecodeError(&amp;quot;expected comma or &amp;#039;}&amp;#039;&amp;quot;, text, i, etc)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      i = skip_whitespace(text, i + 1)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   self:onDecodeError(&amp;quot;unclosed &amp;#039;{&amp;#039;&amp;quot;, text, start, etc)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function grok_array(self, text, start, etc)&lt;br /&gt;
   if text:sub(start,start) ~= &amp;#039;[&amp;#039; then&lt;br /&gt;
      self:onDecodeError(&amp;quot;expected &amp;#039;[&amp;#039;&amp;quot;, text, start, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   local i = skip_whitespace(text, start + 1) -- +1 to skip the &amp;#039;[&amp;#039;&lt;br /&gt;
   local VALUE = self.strictTypes and self:newArray { } or { }&lt;br /&gt;
   if text:sub(i,i) == &amp;#039;]&amp;#039; then&lt;br /&gt;
      return VALUE, i + 1&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   local VALUE_INDEX = 1&lt;br /&gt;
&lt;br /&gt;
   local text_len = text:len()&lt;br /&gt;
   while i &amp;lt;= text_len do&lt;br /&gt;
      local val, new_i = grok_one(self, text, i)&lt;br /&gt;
&lt;br /&gt;
      -- can&amp;#039;t table.insert(VALUE, val) here because it&amp;#039;s a no-op if val is nil&lt;br /&gt;
      VALUE[VALUE_INDEX] = val&lt;br /&gt;
      VALUE_INDEX = VALUE_INDEX + 1&lt;br /&gt;
&lt;br /&gt;
      i = skip_whitespace(text, new_i)&lt;br /&gt;
&lt;br /&gt;
      --&lt;br /&gt;
      -- Expect now either &amp;#039;]&amp;#039; to end things, or a &amp;#039;,&amp;#039; to allow us to continue.&lt;br /&gt;
      --&lt;br /&gt;
      local c = text:sub(i,i)&lt;br /&gt;
      if c == &amp;#039;]&amp;#039; then&lt;br /&gt;
         return VALUE, i + 1&lt;br /&gt;
      end&lt;br /&gt;
      if text:sub(i, i) ~= &amp;#039;,&amp;#039; then&lt;br /&gt;
         self:onDecodeError(&amp;quot;expected comma or &amp;#039;[&amp;#039;&amp;quot;, text, i, etc)&lt;br /&gt;
      end&lt;br /&gt;
      i = skip_whitespace(text, i + 1)&lt;br /&gt;
   end&lt;br /&gt;
   self:onDecodeError(&amp;quot;unclosed &amp;#039;[&amp;#039;&amp;quot;, text, start, etc)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
grok_one = function(self, text, start, etc)&lt;br /&gt;
   -- Skip any whitespace&lt;br /&gt;
   start = skip_whitespace(text, start)&lt;br /&gt;
&lt;br /&gt;
   if start &amp;gt; text:len() then&lt;br /&gt;
      self:onDecodeError(&amp;quot;unexpected end of string&amp;quot;, text, nil, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if text:find(&amp;#039;^&amp;quot;&amp;#039;, start) then&lt;br /&gt;
      return grok_string(self, text, start, etc)&lt;br /&gt;
&lt;br /&gt;
   elseif text:find(&amp;#039;^[-0123456789 ]&amp;#039;, start) then&lt;br /&gt;
      return grok_number(self, text, start, etc)&lt;br /&gt;
&lt;br /&gt;
   elseif text:find(&amp;#039;^%{&amp;#039;, start) then&lt;br /&gt;
      return grok_object(self, text, start, etc)&lt;br /&gt;
&lt;br /&gt;
   elseif text:find(&amp;#039;^%[&amp;#039;, start) then&lt;br /&gt;
      return grok_array(self, text, start, etc)&lt;br /&gt;
&lt;br /&gt;
   elseif text:find(&amp;#039;^true&amp;#039;, start) then&lt;br /&gt;
      return true, start + 4&lt;br /&gt;
&lt;br /&gt;
   elseif text:find(&amp;#039;^false&amp;#039;, start) then&lt;br /&gt;
      return false, start + 5&lt;br /&gt;
&lt;br /&gt;
   elseif text:find(&amp;#039;^null&amp;#039;, start) then&lt;br /&gt;
      return nil, start + 4&lt;br /&gt;
&lt;br /&gt;
   else&lt;br /&gt;
      self:onDecodeError(&amp;quot;can&amp;#039;t parse JSON&amp;quot;, text, start, etc)&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:decode(text, etc)&lt;br /&gt;
   if type(self) ~= &amp;#039;table&amp;#039; or self.__index ~= OBJDEF then&lt;br /&gt;
      OBJDEF:onDecodeError(&amp;quot;JSON:decode must be called in method format&amp;quot;, nil, nil, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if text == nil then&lt;br /&gt;
      self:onDecodeOfNilError(string.format(&amp;quot;nil passed to JSON:decode()&amp;quot;), nil, nil, etc)&lt;br /&gt;
   elseif type(text) ~= &amp;#039;string&amp;#039; then&lt;br /&gt;
      self:onDecodeError(string.format(&amp;quot;expected string argument to JSON:decode(), got %s&amp;quot;, type(text)), nil, nil, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if text:match(&amp;#039;^%s*$&amp;#039;) then&lt;br /&gt;
      return nil&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if text:match(&amp;#039;^%s*&amp;lt;&amp;#039;) then&lt;br /&gt;
      -- Can&amp;#039;t be JSON... we&amp;#039;ll assume it&amp;#039;s HTML&lt;br /&gt;
      self:onDecodeOfHTMLError(string.format(&amp;quot;html passed to JSON:decode()&amp;quot;), text, nil, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   --&lt;br /&gt;
   -- Ensure that it&amp;#039;s not UTF-32 or UTF-16.&lt;br /&gt;
   -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3),&lt;br /&gt;
   -- but this package can&amp;#039;t handle them.&lt;br /&gt;
   --&lt;br /&gt;
   if text:sub(1,1):byte() == 0 or (text:len() &amp;gt;= 2 and text:sub(2,2):byte() == 0) then&lt;br /&gt;
      self:onDecodeError(&amp;quot;JSON package groks only UTF-8, sorry&amp;quot;, text, nil, etc)&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   local success, value = pcall(grok_one, self, text, 1, etc)&lt;br /&gt;
&lt;br /&gt;
   if success then&lt;br /&gt;
      return value&lt;br /&gt;
   else&lt;br /&gt;
      -- if JSON:onDecodeError() didn&amp;#039;t abort out of the pcall, we&amp;#039;ll have received the error message here as &amp;quot;value&amp;quot;, so pass it along as an assert.&lt;br /&gt;
      if self.assert then&lt;br /&gt;
         self.assert(false, value)&lt;br /&gt;
      else&lt;br /&gt;
         assert(false, value)&lt;br /&gt;
      end&lt;br /&gt;
      -- and if we&amp;#039;re still here, return a nil and throw the error message on as a second arg&lt;br /&gt;
      return nil, value&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function backslash_replacement_function(c)&lt;br /&gt;
   if c == &amp;quot;\n&amp;quot; then&lt;br /&gt;
      return &amp;quot;\\n&amp;quot;&lt;br /&gt;
   elseif c == &amp;quot;\r&amp;quot; then&lt;br /&gt;
      return &amp;quot;\\r&amp;quot;&lt;br /&gt;
   elseif c == &amp;quot;\t&amp;quot; then&lt;br /&gt;
      return &amp;quot;\\t&amp;quot;&lt;br /&gt;
   elseif c == &amp;quot;\b&amp;quot; then&lt;br /&gt;
      return &amp;quot;\\b&amp;quot;&lt;br /&gt;
   elseif c == &amp;quot;\f&amp;quot; then&lt;br /&gt;
      return &amp;quot;\\f&amp;quot;&lt;br /&gt;
   elseif c == &amp;#039;&amp;quot;&amp;#039; then&lt;br /&gt;
      return &amp;#039;\\&amp;quot;&amp;#039;&lt;br /&gt;
   elseif c == &amp;#039;\\&amp;#039; then&lt;br /&gt;
      return &amp;#039;\\\\&amp;#039;&lt;br /&gt;
   else&lt;br /&gt;
      return string.format(&amp;quot;\\u%04x&amp;quot;, c:byte())&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local chars_to_be_escaped_in_JSON_string&lt;br /&gt;
   = &amp;#039;[&amp;#039;&lt;br /&gt;
   ..    &amp;#039;&amp;quot;&amp;#039;    -- class sub-pattern to match a double quote&lt;br /&gt;
   ..    &amp;#039;%\\&amp;#039;  -- class sub-pattern to match a backslash&lt;br /&gt;
   ..    &amp;#039;%z&amp;#039;   -- class sub-pattern to match a null&lt;br /&gt;
   ..    &amp;#039;\001&amp;#039; .. &amp;#039;-&amp;#039; .. &amp;#039;\031&amp;#039; -- class sub-pattern to match control characters&lt;br /&gt;
   .. &amp;#039;]&amp;#039;&lt;br /&gt;
&lt;br /&gt;
local function json_string_literal(value)&lt;br /&gt;
   local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function)&lt;br /&gt;
   return &amp;#039;&amp;quot;&amp;#039; .. newval .. &amp;#039;&amp;quot;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function object_or_array(self, T, etc)&lt;br /&gt;
   --&lt;br /&gt;
   -- We need to inspect all the keys... if there are any strings, we&amp;#039;ll convert to a JSON&lt;br /&gt;
   -- object. If there are only numbers, it&amp;#039;s a JSON array.&lt;br /&gt;
   --&lt;br /&gt;
   -- If we&amp;#039;ll be converting to a JSON object, we&amp;#039;ll want to sort the keys so that the&lt;br /&gt;
   -- end result is deterministic.&lt;br /&gt;
   --&lt;br /&gt;
   local string_keys = { }&lt;br /&gt;
   local number_keys = { }&lt;br /&gt;
   local number_keys_must_be_strings = false&lt;br /&gt;
   local maximum_number_key&lt;br /&gt;
&lt;br /&gt;
   for key in pairs(T) do&lt;br /&gt;
      if type(key) == &amp;#039;string&amp;#039; then&lt;br /&gt;
         table.insert(string_keys, key)&lt;br /&gt;
      elseif type(key) == &amp;#039;number&amp;#039; then&lt;br /&gt;
         table.insert(number_keys, key)&lt;br /&gt;
         if key &amp;lt;= 0 or key &amp;gt;= math.huge then&lt;br /&gt;
            number_keys_must_be_strings = true&lt;br /&gt;
         elseif not maximum_number_key or key &amp;gt; maximum_number_key then&lt;br /&gt;
            maximum_number_key = key&lt;br /&gt;
         end&lt;br /&gt;
      else&lt;br /&gt;
         self:onEncodeError(&amp;quot;can&amp;#039;t encode table with a key of type &amp;quot; .. type(key), etc)&lt;br /&gt;
      end&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if #string_keys == 0 and not number_keys_must_be_strings then&lt;br /&gt;
      --&lt;br /&gt;
      -- An empty table, or a numeric-only array&lt;br /&gt;
      --&lt;br /&gt;
      if #number_keys &amp;gt; 0 then&lt;br /&gt;
         return nil, maximum_number_key -- an array&lt;br /&gt;
      elseif tostring(T) == &amp;quot;JSON array&amp;quot; then&lt;br /&gt;
         return nil&lt;br /&gt;
      elseif tostring(T) == &amp;quot;JSON object&amp;quot; then&lt;br /&gt;
         return { }&lt;br /&gt;
      else&lt;br /&gt;
         -- have to guess, so we&amp;#039;ll pick array, since empty arrays are likely more common than empty objects&lt;br /&gt;
         return nil&lt;br /&gt;
      end&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   table.sort(string_keys)&lt;br /&gt;
&lt;br /&gt;
   local map&lt;br /&gt;
   if #number_keys &amp;gt; 0 then&lt;br /&gt;
      --&lt;br /&gt;
      -- If we&amp;#039;re here then we have either mixed string/number keys, or numbers inappropriate for a JSON array&lt;br /&gt;
      -- It&amp;#039;s not ideal, but we&amp;#039;ll turn the numbers into strings so that we can at least create a JSON object.&lt;br /&gt;
      --&lt;br /&gt;
&lt;br /&gt;
      if self.noKeyConversion then&lt;br /&gt;
         self:onEncodeError(&amp;quot;a table with both numeric and string keys could be an object or array; aborting&amp;quot;, etc)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      --&lt;br /&gt;
      -- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings&lt;br /&gt;
      --&lt;br /&gt;
      map = { }&lt;br /&gt;
      for key, val in pairs(T) do&lt;br /&gt;
         map[key] = val&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      table.sort(number_keys)&lt;br /&gt;
&lt;br /&gt;
      --&lt;br /&gt;
      -- Throw numeric keys in there as strings&lt;br /&gt;
      --&lt;br /&gt;
      for _, number_key in ipairs(number_keys) do&lt;br /&gt;
         local string_key = tostring(number_key)&lt;br /&gt;
         if map[string_key] == nil then&lt;br /&gt;
            table.insert(string_keys , string_key)&lt;br /&gt;
            map[string_key] = T[number_key]&lt;br /&gt;
         else&lt;br /&gt;
            self:onEncodeError(&amp;quot;conflict converting table with mixed-type keys into a JSON object: key &amp;quot; .. number_key .. &amp;quot; exists both as a string and a number.&amp;quot;, etc)&lt;br /&gt;
         end&lt;br /&gt;
      end&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   return string_keys, nil, map&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--&lt;br /&gt;
-- Encode&lt;br /&gt;
--&lt;br /&gt;
local encode_value -- must predeclare because it calls itself&lt;br /&gt;
function encode_value(self, value, parents, etc, indent) -- non-nil indent means pretty-printing&lt;br /&gt;
&lt;br /&gt;
   if value == nil then&lt;br /&gt;
      return &amp;#039;null&amp;#039;&lt;br /&gt;
&lt;br /&gt;
   elseif type(value) == &amp;#039;string&amp;#039; then&lt;br /&gt;
      return json_string_literal(value)&lt;br /&gt;
&lt;br /&gt;
   elseif type(value) == &amp;#039;number&amp;#039; then&lt;br /&gt;
      if value ~= value then&lt;br /&gt;
         --&lt;br /&gt;
         -- NaN (Not a Number).&lt;br /&gt;
         -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option.&lt;br /&gt;
         --&lt;br /&gt;
         return &amp;quot;null&amp;quot;&lt;br /&gt;
      elseif value &amp;gt;= math.huge then&lt;br /&gt;
         --&lt;br /&gt;
         -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should&lt;br /&gt;
         -- really be a package option. Note: at least with some implementations, positive infinity&lt;br /&gt;
         -- is both &amp;quot;&amp;gt;= math.huge&amp;quot; and &amp;quot;&amp;lt;= -math.huge&amp;quot;, which makes no sense but that&amp;#039;s how it is.&lt;br /&gt;
         -- Negative infinity is properly &amp;quot;&amp;lt;= -math.huge&amp;quot;. So, we must be sure to check the &amp;quot;&amp;gt;=&amp;quot;&lt;br /&gt;
         -- case first.&lt;br /&gt;
         --&lt;br /&gt;
         return &amp;quot;1e+9999&amp;quot;&lt;br /&gt;
      elseif value &amp;lt;= -math.huge then&lt;br /&gt;
         --&lt;br /&gt;
         -- Negative infinity.&lt;br /&gt;
         -- JSON has no INF, so we have to fudge the best we can. This should really be a package option.&lt;br /&gt;
         --&lt;br /&gt;
         return &amp;quot;-1e+9999&amp;quot;&lt;br /&gt;
      else&lt;br /&gt;
         return tostring(value)&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
   elseif type(value) == &amp;#039;boolean&amp;#039; then&lt;br /&gt;
      return tostring(value)&lt;br /&gt;
&lt;br /&gt;
   elseif type(value) ~= &amp;#039;table&amp;#039; then&lt;br /&gt;
      self:onEncodeError(&amp;quot;can&amp;#039;t convert &amp;quot; .. type(value) .. &amp;quot; to JSON&amp;quot;, etc)&lt;br /&gt;
&lt;br /&gt;
   else&lt;br /&gt;
      --&lt;br /&gt;
      -- A table to be converted to either a JSON object or array.&lt;br /&gt;
      --&lt;br /&gt;
      local T = value&lt;br /&gt;
&lt;br /&gt;
      if parents[T] then&lt;br /&gt;
         self:onEncodeError(&amp;quot;table &amp;quot; .. tostring(T) .. &amp;quot; is a child of itself&amp;quot;, etc)&lt;br /&gt;
      else&lt;br /&gt;
         parents[T] = true&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      local result_value&lt;br /&gt;
&lt;br /&gt;
      local object_keys, maximum_number_key, map = object_or_array(self, T, etc)&lt;br /&gt;
      if maximum_number_key then&lt;br /&gt;
         --&lt;br /&gt;
         -- An array...&lt;br /&gt;
         --&lt;br /&gt;
         local ITEMS = { }&lt;br /&gt;
         for i = 1, maximum_number_key do&lt;br /&gt;
            table.insert(ITEMS, encode_value(self, T[i], parents, etc, indent))&lt;br /&gt;
         end&lt;br /&gt;
&lt;br /&gt;
         if indent then&lt;br /&gt;
            result_value = &amp;quot;[ &amp;quot; .. table.concat(ITEMS, &amp;quot;, &amp;quot;) .. &amp;quot; ]&amp;quot;&lt;br /&gt;
         else&lt;br /&gt;
            result_value = &amp;quot;[&amp;quot; .. table.concat(ITEMS, &amp;quot;,&amp;quot;) .. &amp;quot;]&amp;quot;&lt;br /&gt;
         end&lt;br /&gt;
&lt;br /&gt;
      elseif object_keys then&lt;br /&gt;
         --&lt;br /&gt;
         -- An object&lt;br /&gt;
         --&lt;br /&gt;
         local TT = map or T&lt;br /&gt;
&lt;br /&gt;
         if indent then&lt;br /&gt;
&lt;br /&gt;
            local KEYS = { }&lt;br /&gt;
            local max_key_length = 0&lt;br /&gt;
            for _, key in ipairs(object_keys) do&lt;br /&gt;
               local encoded = encode_value(self, tostring(key), parents, etc, &amp;quot;&amp;quot;)&lt;br /&gt;
               max_key_length = math.max(max_key_length, #encoded)&lt;br /&gt;
               table.insert(KEYS, encoded)&lt;br /&gt;
            end&lt;br /&gt;
            local key_indent = indent .. &amp;quot;    &amp;quot;&lt;br /&gt;
            local subtable_indent = indent .. string.rep(&amp;quot; &amp;quot;, max_key_length + 2 + 4)&lt;br /&gt;
            local FORMAT = &amp;quot;%s%&amp;quot; .. string.format(&amp;quot;%d&amp;quot;, max_key_length) .. &amp;quot;s: %s&amp;quot;&lt;br /&gt;
&lt;br /&gt;
            local COMBINED_PARTS = { }&lt;br /&gt;
            for i, key in ipairs(object_keys) do&lt;br /&gt;
               local encoded_val = encode_value(self, TT[key], parents, etc, subtable_indent)&lt;br /&gt;
               table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val))&lt;br /&gt;
            end&lt;br /&gt;
            result_value = &amp;quot;{\n&amp;quot; .. table.concat(COMBINED_PARTS, &amp;quot;,\n&amp;quot;) .. &amp;quot;\n&amp;quot; .. indent .. &amp;quot;}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
         else&lt;br /&gt;
&lt;br /&gt;
            local PARTS = { }&lt;br /&gt;
            for _, key in ipairs(object_keys) do&lt;br /&gt;
               local encoded_val = encode_value(self, TT[key],       parents, etc, indent)&lt;br /&gt;
               local encoded_key = encode_value(self, tostring(key), parents, etc, indent)&lt;br /&gt;
               table.insert(PARTS, string.format(&amp;quot;%s:%s&amp;quot;, encoded_key, encoded_val))&lt;br /&gt;
            end&lt;br /&gt;
            result_value = &amp;quot;{&amp;quot; .. table.concat(PARTS, &amp;quot;,&amp;quot;) .. &amp;quot;}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
         end&lt;br /&gt;
      else&lt;br /&gt;
         --&lt;br /&gt;
         -- An empty array/object... we&amp;#039;ll treat it as an array, though it should really be an option&lt;br /&gt;
         --&lt;br /&gt;
         result_value = &amp;quot;[]&amp;quot;&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      parents[T] = false&lt;br /&gt;
      return result_value&lt;br /&gt;
   end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:encode(value, etc)&lt;br /&gt;
   if type(self) ~= &amp;#039;table&amp;#039; or self.__index ~= OBJDEF then&lt;br /&gt;
      OBJDEF:onEncodeError(&amp;quot;JSON:encode must be called in method format&amp;quot;, etc)&lt;br /&gt;
   end&lt;br /&gt;
   return encode_value(self, value, {}, etc, nil)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:encode_pretty(value, etc)&lt;br /&gt;
   if type(self) ~= &amp;#039;table&amp;#039; or self.__index ~= OBJDEF then&lt;br /&gt;
      OBJDEF:onEncodeError(&amp;quot;JSON:encode_pretty must be called in method format&amp;quot;, etc)&lt;br /&gt;
   end&lt;br /&gt;
   return encode_value(self, value, {}, etc, &amp;quot;&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function OBJDEF.__tostring()&lt;br /&gt;
   return &amp;quot;JSON encode/decode package&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
OBJDEF.__index = OBJDEF&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:new(args)&lt;br /&gt;
   local new = { }&lt;br /&gt;
&lt;br /&gt;
   if args then&lt;br /&gt;
      for key, val in pairs(args) do&lt;br /&gt;
         new[key] = val&lt;br /&gt;
      end&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   return setmetatable(new, OBJDEF)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return OBJDEF:new()&lt;br /&gt;
&lt;br /&gt;
--&lt;br /&gt;
-- Version history:&lt;br /&gt;
--&lt;br /&gt;
--   20140911.12   Minor lua cleanup.&lt;br /&gt;
--                 Fixed internal reference to &amp;#039;JSON.noKeyConversion&amp;#039; to reference &amp;#039;self&amp;#039; instead of &amp;#039;JSON&amp;#039;.&lt;br /&gt;
--                 (Thanks to SmugMug&amp;#039;s David Parry for these.)&lt;br /&gt;
--&lt;br /&gt;
--   20140418.11   JSON nulls embedded within an array were being ignored, such that&lt;br /&gt;
--                     [&amp;quot;1&amp;quot;,null,null,null,null,null,&amp;quot;seven&amp;quot;],&lt;br /&gt;
--                 would return&lt;br /&gt;
--                     {1,&amp;quot;seven&amp;quot;}&lt;br /&gt;
--                 It&amp;#039;s now fixed to properly return&lt;br /&gt;
--                     {1, nil, nil, nil, nil, nil, &amp;quot;seven&amp;quot;}&lt;br /&gt;
--                 Thanks to &amp;quot;haddock&amp;quot; for catching the error.&lt;br /&gt;
--&lt;br /&gt;
--   20140116.10   The user&amp;#039;s JSON.assert() wasn&amp;#039;t always being used. Thanks to &amp;quot;blue&amp;quot; for the heads up.&lt;br /&gt;
--&lt;br /&gt;
--   20131118.9    Update for Lua 5.3... it seems that tostring(2/1) produces &amp;quot;2.0&amp;quot; instead of &amp;quot;2&amp;quot;,&lt;br /&gt;
--                 and this caused some problems.&lt;br /&gt;
--&lt;br /&gt;
--   20131031.8    Unified the code for encode() and encode_pretty(); they had been stupidly separate,&lt;br /&gt;
--                 and had of course diverged (encode_pretty didn&amp;#039;t get the fixes that encode got, so&lt;br /&gt;
--                 sometimes produced incorrect results; thanks to Mattie for the heads up).&lt;br /&gt;
--&lt;br /&gt;
--                 Handle encoding tables with non-positive numeric keys (unlikely, but possible).&lt;br /&gt;
--&lt;br /&gt;
--                 If a table has both numeric and string keys, or its numeric keys are inappropriate&lt;br /&gt;
--                 (such as being non-positive or infinite), the numeric keys are turned into&lt;br /&gt;
--                 string keys appropriate for a JSON object. So, as before,&lt;br /&gt;
--                         JSON:encode({ &amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, &amp;quot;three&amp;quot; })&lt;br /&gt;
--                 produces the array&lt;br /&gt;
--                         [&amp;quot;one&amp;quot;,&amp;quot;two&amp;quot;,&amp;quot;three&amp;quot;]&lt;br /&gt;
--                 but now something with mixed key types like&lt;br /&gt;
--                         JSON:encode({ &amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, &amp;quot;three&amp;quot;, SOMESTRING = &amp;quot;some string&amp;quot; }))&lt;br /&gt;
--                 instead of throwing an error produces an object:&lt;br /&gt;
--                         {&amp;quot;1&amp;quot;:&amp;quot;one&amp;quot;,&amp;quot;2&amp;quot;:&amp;quot;two&amp;quot;,&amp;quot;3&amp;quot;:&amp;quot;three&amp;quot;,&amp;quot;SOMESTRING&amp;quot;:&amp;quot;some string&amp;quot;}&lt;br /&gt;
--&lt;br /&gt;
--                 To maintain the prior throw-an-error semantics, set&lt;br /&gt;
--                      JSON.noKeyConversion = true&lt;br /&gt;
--                 &lt;br /&gt;
--   20131004.7    Release under a Creative Commons CC-BY license, which I should have done from day one, sorry.&lt;br /&gt;
--&lt;br /&gt;
--   20130120.6    Comment update: added a link to the specific page on my blog where this code can&lt;br /&gt;
--                 be found, so that folks who come across the code outside of my blog can find updates&lt;br /&gt;
--                 more easily.&lt;br /&gt;
--&lt;br /&gt;
--   20111207.5    Added support for the &amp;#039;etc&amp;#039; arguments, for better error reporting.&lt;br /&gt;
--&lt;br /&gt;
--   20110731.4    More feedback from David Kolf on how to make the tests for Nan/Infinity system independent.&lt;br /&gt;
--&lt;br /&gt;
--   20110730.3    Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules:&lt;br /&gt;
--&lt;br /&gt;
--                   * When encoding lua for JSON, Sparse numeric arrays are now handled by&lt;br /&gt;
--                     spitting out full arrays, such that&lt;br /&gt;
--                        JSON:encode({&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, [10] = &amp;quot;ten&amp;quot;})&lt;br /&gt;
--                     returns&lt;br /&gt;
--                        [&amp;quot;one&amp;quot;,&amp;quot;two&amp;quot;,null,null,null,null,null,null,null,&amp;quot;ten&amp;quot;]&lt;br /&gt;
--&lt;br /&gt;
--                     In 20100810.2 and earlier, only up to the first non-null value would have been retained.&lt;br /&gt;
--&lt;br /&gt;
--                   * When encoding lua for JSON, numeric value NaN gets spit out as null, and infinity as &amp;quot;1+e9999&amp;quot;.&lt;br /&gt;
--                     Version 20100810.2 and earlier created invalid JSON in both cases.&lt;br /&gt;
--&lt;br /&gt;
--                   * Unicode surrogate pairs are now detected when decoding JSON.&lt;br /&gt;
--&lt;br /&gt;
--   20100810.2    added some checking to ensure that an invalid Unicode character couldn&amp;#039;t leak in to the UTF-8 encoding&lt;br /&gt;
--&lt;br /&gt;
--   20100731.1    initial public release&lt;/div&gt;</summary>
		<author><name>Karaby</name></author>
	</entry>
</feed>