<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://themidnight.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AJSON</id>
	<title>Module:JSON - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://themidnight.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AJSON"/>
	<link rel="alternate" type="text/html" href="https://themidnight.wiki/index.php?title=Module:JSON&amp;action=history"/>
	<updated>2026-04-03T21:15:02Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.41.1</generator>
	<entry>
		<id>https://themidnight.wiki/index.php?title=Module:JSON&amp;diff=7746&amp;oldid=prev</id>
		<title>Timothy: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://themidnight.wiki/index.php?title=Module:JSON&amp;diff=7746&amp;oldid=prev"/>
		<updated>2023-06-12T15:53:36Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 11:53, 12 June 2023&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Timothy</name></author>
	</entry>
	<entry>
		<id>https://themidnight.wiki/index.php?title=Module:JSON&amp;diff=7745&amp;oldid=prev</id>
		<title>wikimedia&gt;4nn1l2: Changed protection level for &quot;Module:JSON&quot;: downgraded protection level per consensus ([Move=Allow only template editors and administrators] (indefinite) [Edit=Allow only template editors and administrators] (indefinite))</title>
		<link rel="alternate" type="text/html" href="https://themidnight.wiki/index.php?title=Module:JSON&amp;diff=7745&amp;oldid=prev"/>
		<updated>2019-07-16T11:19:14Z</updated>

		<summary type="html">&lt;p&gt;Changed protection level for &amp;quot;&lt;a href=&quot;/wiki/Module:JSON&quot; title=&quot;Module:JSON&quot;&gt;Module:JSON&lt;/a&gt;&amp;quot;: downgraded protection level per &lt;a href=&quot;/wiki/Special:PermanentLink/357548402&quot; title=&quot;Special:PermanentLink/357548402&quot;&gt;consensus&lt;/a&gt; ([Move=Allow only template editors and administrators] (indefinite) [Edit=Allow only template editors and administrators] (indefinite))&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- -*- coding: utf-8 -*-&lt;br /&gt;
--&lt;br /&gt;
-- Copyright 2010-2012 Jeffrey Friedl&lt;br /&gt;
-- http://regex.info/blog/&lt;br /&gt;
--&lt;br /&gt;
local VERSION = 20111207.5  -- version history at end of file&lt;br /&gt;
local OBJDEF = { VERSION = VERSION }&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 Lua table came from, or guarantee decode-encode round-trip&lt;br /&gt;
--   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;
--&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;
&lt;br /&gt;
local author = &amp;quot;-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json), version &amp;quot; .. tostring(VERSION) .. &amp;quot; ]-&amp;quot;&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;
   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_start, 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 not 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 val, new_i = grok_one(self, text, i)&lt;br /&gt;
&lt;br /&gt;
      VALUE[key] = 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 not 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 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;
      table.insert(VALUE, val)&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;
   if success then&lt;br /&gt;
      return value&lt;br /&gt;
   else&lt;br /&gt;
      -- should never get here... JSON parse errors should have been caught earlier&lt;br /&gt;
      assert(false, value)&lt;br /&gt;
      return nil&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 seen_number_key = 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;number&amp;#039; then&lt;br /&gt;
         seen_number_key = true&lt;br /&gt;
         if not maximum_number_key or maximum_number_key &amp;lt; key then&lt;br /&gt;
            maximum_number_key = key&lt;br /&gt;
         end&lt;br /&gt;
      elseif type(key) == &amp;#039;string&amp;#039; then&lt;br /&gt;
         table.insert(string_keys, key)&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 seen_number_key and #string_keys &amp;gt; 0 then&lt;br /&gt;
      --&lt;br /&gt;
      -- Mixed key types... don&amp;#039;t know what to do, so bail&lt;br /&gt;
      --&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;
&lt;br /&gt;
   elseif #string_keys == 0  then&lt;br /&gt;
      --&lt;br /&gt;
      -- An array&lt;br /&gt;
      --&lt;br /&gt;
      if seen_number_key then&lt;br /&gt;
         return nil, maximum_number_key -- an array&lt;br /&gt;
      else&lt;br /&gt;
         --&lt;br /&gt;
         -- An empty table...&lt;br /&gt;
         --&lt;br /&gt;
         if 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;
   else&lt;br /&gt;
      --&lt;br /&gt;
      -- An object, so return a list of keys&lt;br /&gt;
      --&lt;br /&gt;
      table.sort(string_keys)&lt;br /&gt;
      return string_keys&lt;br /&gt;
   end&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)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   if value == nil then&lt;br /&gt;
      return &amp;#039;null&amp;#039;&lt;br /&gt;
   end&lt;br /&gt;
&lt;br /&gt;
   if type(value) == &amp;#039;string&amp;#039; then&lt;br /&gt;
      return json_string_literal(value)&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;
   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 = 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))&lt;br /&gt;
         end&lt;br /&gt;
&lt;br /&gt;
         result_value = &amp;quot;[&amp;quot; .. table.concat(ITEMS, &amp;quot;,&amp;quot;) .. &amp;quot;]&amp;quot;&lt;br /&gt;
      elseif object_keys then&lt;br /&gt;
         --&lt;br /&gt;
         -- An object&lt;br /&gt;
         --&lt;br /&gt;
&lt;br /&gt;
         --&lt;br /&gt;
         -- We&amp;#039;ll always sort the keys, so that comparisons can be made on&lt;br /&gt;
         -- the results, etc. The actual order is not particularly&lt;br /&gt;
         -- important (e.g. it doesn&amp;#039;t matter what character set we sort&lt;br /&gt;
         -- as); it&amp;#039;s only important that it be deterministic... the same&lt;br /&gt;
         -- every time.&lt;br /&gt;
         --&lt;br /&gt;
         local PARTS = { }&lt;br /&gt;
         for _, key in ipairs(object_keys) do&lt;br /&gt;
            local encoded_key = encode_value(self, tostring(key), parents, etc)&lt;br /&gt;
            local encoded_val = encode_value(self, T[key],        parents, etc)&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;
      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;
local encode_pretty_value -- must predeclare because it calls itself&lt;br /&gt;
function encode_pretty_value(self, value, parents, indent, etc)&lt;br /&gt;
&lt;br /&gt;
   if 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;
      return tostring(value)&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;nil&amp;#039; then&lt;br /&gt;
      return &amp;#039;null&amp;#039;&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;
      end&lt;br /&gt;
      parents[T] = true&lt;br /&gt;
&lt;br /&gt;
      local result_value&lt;br /&gt;
&lt;br /&gt;
      local object_keys = object_or_array(self, T, etc)&lt;br /&gt;
      if not object_keys then&lt;br /&gt;
         --&lt;br /&gt;
         -- An array...&lt;br /&gt;
         --&lt;br /&gt;
         local ITEMS = { }&lt;br /&gt;
         for i = 1, #T do&lt;br /&gt;
            table.insert(ITEMS, encode_pretty_value(self, T[i], parents, indent, etc))&lt;br /&gt;
         end&lt;br /&gt;
&lt;br /&gt;
         result_value = &amp;quot;[ &amp;quot; .. table.concat(ITEMS, &amp;quot;, &amp;quot;) .. &amp;quot; ]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
      else&lt;br /&gt;
&lt;br /&gt;
         --&lt;br /&gt;
         -- An object -- can keys be numbers?&lt;br /&gt;
         --&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_pretty_value(self, tostring(key), parents, &amp;quot;&amp;quot;, etc)&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; .. tostring(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_pretty_value(self, T[key], parents, subtable_indent, etc)&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;
      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;
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;
&lt;br /&gt;
   local parents = {}&lt;br /&gt;
   return encode_value(self, value, parents, etc)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function OBJDEF:encode_pretty(value, etc)&lt;br /&gt;
   local parents = {}&lt;br /&gt;
   local subtable_indent = &amp;quot;&amp;quot;&lt;br /&gt;
   return encode_pretty_value(self, value, parents, subtable_indent, etc)&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;
--   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;br /&gt;
--&lt;/div&gt;</summary>
		<author><name>wikimedia&gt;4nn1l2</name></author>
	</entry>
</feed>