From 0cbd6c3636c96ef95ca247bb6eeb448268901ad4 Mon Sep 17 00:00:00 2001 From: KyleN <25726366+kyl3n@users.noreply.github.com> Date: Fri, 26 Jul 2019 19:51:39 -0600 Subject: [PATCH 01/12] Fixed locale floating point character When running this code in locales that do not use a period for floating point integers, an error would occur. This is now resolved. --- json.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/json.lua b/json.lua index 098e7b2..4e4b71a 100644 --- a/json.lua +++ b/json.lua @@ -154,6 +154,8 @@ local space_chars = create_set(" ", "\t", "\r", "\n") local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") local literals = create_set("true", "false", "null") +local locale_character = (string.match(tostring(tonumber(1.1)),'%p')) -- return the locale punctuation character according to the locale + local literal_map = { [ "true" ] = true, @@ -271,6 +273,7 @@ end local function parse_number(str, i) local x = next_char(str, i, delim_chars) local s = str:sub(i, x - 1) + s = s:gsub('%p',locale_character) -- replace the locale punctuation character in the string with the correct one local n = tonumber(s) if not n then decode_error(str, i, "invalid number '" .. s .. "'") From a8d5dc7e9d7069192ae2ff86d5beed0b6b0bba39 Mon Sep 17 00:00:00 2001 From: KyleN <25726366+kyl3n@users.noreply.github.com> Date: Mon, 26 Aug 2019 14:17:03 -0600 Subject: [PATCH 02/12] replace only comma or decimal in floating point integer previous version was replacing minus sign in negative numbers in error. --- json.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/json.lua b/json.lua index 4e4b71a..e0513e6 100644 --- a/json.lua +++ b/json.lua @@ -154,7 +154,7 @@ local space_chars = create_set(" ", "\t", "\r", "\n") local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") local literals = create_set("true", "false", "null") -local locale_character = (string.match(tostring(tonumber(1.1)),'%p')) -- return the locale punctuation character according to the locale +local locale_character = (string.match(tostring(1/2),'%p')) -- return point decimal or comma decimal according to the locale local literal_map = { @@ -273,15 +273,13 @@ end local function parse_number(str, i) local x = next_char(str, i, delim_chars) local s = str:sub(i, x - 1) - s = s:gsub('%p',locale_character) -- replace the locale punctuation character in the string with the correct one + s = s:gsub('[%.%,]',locale_character) -- replace the comma decimal or point decimal with the correct one according to the locale local n = tonumber(s) if not n then decode_error(str, i, "invalid number '" .. s .. "'") end return n, x end - - local function parse_literal(str, i) local x = next_char(str, i, delim_chars) local word = str:sub(i, x - 1) From ad6ac0fd8e5b8ceaea17fab2868d0620099e882b Mon Sep 17 00:00:00 2001 From: aryajur Date: Sun, 19 Jul 2020 15:26:25 -0700 Subject: [PATCH 03/12] Improved table encoding code to handle sparse arrays. Floating point numbers are encoding improved. --- json.lua | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/json.lua b/json.lua index 711ef78..3ff9893 100644 --- a/json.lua +++ b/json.lua @@ -64,22 +64,24 @@ local function encode_table(val, stack) if stack[val] then error("circular reference") end stack[val] = true + -- Check whether to treat as a array or object + local array = true + local length = 0 + for k in pairs(val) do + if type(k) ~= "number" or k<=0 then + array = nil + break -- Treat as object + else + if k > length then + length = k + end + end + end - if rawget(val, 1) ~= nil or next(val) == nil then - -- Treat as array -- check keys are valid and it is not sparse - local n = 0 - for k in pairs(val) do - if type(k) ~= "number" then - error("invalid table: mixed or invalid key types") - end - n = n + 1 - end - if n ~= #val then - error("invalid table: sparse array") - end + if array then -- Encode - for i, v in ipairs(val) do - table.insert(res, encode(v, stack)) + for i=1,length do + table.insert(res, encode(val[i], stack)) end stack[val] = nil return "[" .. table.concat(res, ",") .. "]" @@ -87,9 +89,11 @@ local function encode_table(val, stack) else -- Treat as an object for k, v in pairs(val) do + --[[ if type(k) ~= "string" then error("invalid table: mixed or invalid key types") end + ]] table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) end stack[val] = nil @@ -108,7 +112,7 @@ local function encode_number(val) if val ~= val or val <= -math.huge or val >= math.huge then error("unexpected number value '" .. tostring(val) .. "'") end - return string.format("%.14g", val) + return tostring(val) end From 32a59c89b0986796bc1baa15b44168e10c66f691 Mon Sep 17 00:00:00 2001 From: aryajur Date: Tue, 18 Aug 2020 21:42:49 -0700 Subject: [PATCH 04/12] Updated code to allow array specifications using table.pack function. The array detection code now allows for the array to have a key n with a number value to detect the array length. --- json.lua | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/json.lua b/json.lua index 3ff9893..3bf14f9 100644 --- a/json.lua +++ b/json.lua @@ -67,18 +67,25 @@ local function encode_table(val, stack) -- Check whether to treat as a array or object local array = true local length = 0 - for k in pairs(val) do - if type(k) ~= "number" or k<=0 then - array = nil - break -- Treat as object - else - if k > length then - length = k + local nLen + for k,v in pairs(val) do + if (type(k) ~= "number" or k<=0) and not (k == "n" and type(v) == "number") then + array = nil + break -- Treat as object + else + if k > length then + length = k + end + if k == "n" and type(v) == "number" then + nLen = v + end end - end end if array then + if nLen > length then + length = nLen + end -- Encode for i=1,length do table.insert(res, encode(val[i], stack)) From f521e4ac03c231be2b526c237c0325959a73ac66 Mon Sep 17 00:00:00 2001 From: aryajur Date: Sat, 22 Aug 2020 22:25:28 -0700 Subject: [PATCH 05/12] nLen initialization fixed --- json.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json.lua b/json.lua index 3bf14f9..0d89492 100644 --- a/json.lua +++ b/json.lua @@ -67,7 +67,7 @@ local function encode_table(val, stack) -- Check whether to treat as a array or object local array = true local length = 0 - local nLen + local nLen = 0 for k,v in pairs(val) do if (type(k) ~= "number" or k<=0) and not (k == "n" and type(v) == "number") then array = nil From 6465f08e4d2575f5a72db25d731f8d6f47ea7252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20B=C4=85k?= Date: Wed, 30 Dec 2020 21:04:13 +0100 Subject: [PATCH 06/12] Update info about supporting Lua 5.4 version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 96b9b66..e626e48 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A lightweight JSON library for Lua ## Features -* Implemented in pure Lua: works with 5.1, 5.2, 5.3 and JIT +* Implemented in pure Lua: works with 5.1, 5.2, 5.3, 5.4 and JIT * Fast: generally outperforms other pure Lua JSON implementations ([benchmark scripts](bench/)) * Tiny: around 280sloc, 9kb From 15823a48b31befa5f623134f6d8c57a2f5a603f9 Mon Sep 17 00:00:00 2001 From: aleksanderd Date: Fri, 11 Nov 2022 21:41:19 +0300 Subject: [PATCH 07/12] fix #41: Large numbers serialized incorrectly --- json.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/json.lua b/json.lua index 711ef78..2956e16 100644 --- a/json.lua +++ b/json.lua @@ -108,7 +108,12 @@ local function encode_number(val) if val ~= val or val <= -math.huge or val >= math.huge then error("unexpected number value '" .. tostring(val) .. "'") end - return string.format("%.14g", val) + local intVal = math.tointeger(val) + if intVal == val then + return string.format("%d", intVal) + else + return string.format("%.14g", val) + end end From c7968a00b9d1906f70271ec428ffb0f336d9e8fa Mon Sep 17 00:00:00 2001 From: alexandro-rezakhani <85354050+alexandro-rezakhani@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:25:18 -0500 Subject: [PATCH 08/12] Update json.lua --- json.lua | 80 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/json.lua b/json.lua index 4838b2f..bc4b745 100644 --- a/json.lua +++ b/json.lua @@ -55,6 +55,7 @@ local function encode_nil(val) return "null" end +local json_object_tag = {} local function encode_table(val, stack) local res = {} @@ -64,47 +65,50 @@ local function encode_table(val, stack) if stack[val] then error("circular reference") end stack[val] = true - -- Check whether to treat as a array or object - local array = true - local length = 0 - local nLen = 0 - for k,v in pairs(val) do - if (type(k) ~= "number" or k<=0) and not (k == "n" and type(v) == "number") then - array = nil - break -- Treat as object - else - if k > length then - length = k - end - if k == "n" and type(v) == "number" then - nLen = v - end - end - end - if array then - if nLen > length then - length = nLen - end - -- Encode - for i=1,length do - table.insert(res, encode(val[i], stack)) - end - stack[val] = nil - return "[" .. table.concat(res, ",") .. "]" - - else - -- Treat as an object - for k, v in pairs(val) do - --[[ - if type(k) ~= "string" then - error("invalid table: mixed or invalid key types") + if getmetatable(val) ~= json_object_tag and (rawget(val, 1) ~= nil or next(val) == nil) then + -- Check whether to treat as a array or object + local array = true + local length = 0 + local nLen = 0 + for k,v in pairs(val) do + if (type(k) ~= "number" or k<=0) and not (k == "n" and type(v) == "number") then + array = nil + break -- Treat as object + else + if k > length then + length = k + end + if k == "n" and type(v) == "number" then + nLen = v + end end - ]] - table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) end - stack[val] = nil - return "{" .. table.concat(res, ",") .. "}" + if array then + if nLen > length then + length = nLen + end + -- Encode + for i=1,length do + table.insert(res, encode(val[i], stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + else + -- Treat as an object + for k, v in pairs(val) do + --[[ + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + ]] + if k ~= "_" then + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end end end From 917e8a08c218439005f14ad50170c16df26434b5 Mon Sep 17 00:00:00 2001 From: alexandro-rezakhani <85354050+alexandro-rezakhani@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:58:38 -0500 Subject: [PATCH 09/12] Optimize code Took advice from @Dnsls and @Faserr to optimize code from #34. https://github.com/rxi/json.lua/issues/34 https://github.com/rxi/json.lua/issues/34#issue-900464723 https://github.com/rxi/json.lua/issues/34#issuecomment-1060329470 --- json.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/json.lua b/json.lua index bc4b745..53bc7ef 100644 --- a/json.lua +++ b/json.lua @@ -22,7 +22,7 @@ -- SOFTWARE. -- -local json = { _version = "0.1.2" } +local json = { _version = "0.1.2fix" } ------------------------------------------------------------------------------- -- Encode @@ -233,7 +233,7 @@ end local function parse_string(str, i) - local res = "" + local res = {} local j = i + 1 local k = j @@ -244,25 +244,25 @@ local function parse_string(str, i) decode_error(str, j, "control character in string") elseif x == 92 then -- `\`: Escape - res = res .. str:sub(k, j - 1) + res[#res + 1] = str:sub(k, j - 1) j = j + 1 local c = str:sub(j, j) if c == "u" then local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or str:match("^%x%x%x%x", j + 1) or decode_error(str, j - 1, "invalid unicode escape in string") - res = res .. parse_unicode_escape(hex) + res[#res + 1] = parse_unicode_escape(hex) j = j + #hex else if not escape_chars[c] then decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") end - res = res .. escape_char_map_inv[c] + res[#res + 1] = escape_char_map_inv[c] end k = j + 1 elseif x == 34 then -- `"`: End of string - res = res .. str:sub(k, j - 1) + res[#res + 1] = str:sub(k, j - 1) return res, j + 1 end From 6006d0fcc010c001d4a3b4a25f5fbabb57f0c8d1 Mon Sep 17 00:00:00 2001 From: alexandro-rezakhani <85354050+alexandro-rezakhani@users.noreply.github.com> Date: Sun, 20 Nov 2022 01:10:24 -0500 Subject: [PATCH 10/12] fix #41: Large numbers serialized incorrectly #9 --- json.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/json.lua b/json.lua index 53bc7ef..f65c88d 100644 --- a/json.lua +++ b/json.lua @@ -123,7 +123,12 @@ local function encode_number(val) if val ~= val or val <= -math.huge or val >= math.huge then error("unexpected number value '" .. tostring(val) .. "'") end - return tostring(val) + local intVal = math.tointeger(val) + if intVal == val then + return string.format("%d", intVal) + else + return string.format("%.14g", val) + end end From 83b301510b1b8191879f066865812af760ac34d0 Mon Sep 17 00:00:00 2001 From: alexandro-rezakhani <85354050+alexandro-rezakhani@users.noreply.github.com> Date: Sun, 20 Nov 2022 01:21:55 -0500 Subject: [PATCH 11/12] handle encoding of number based key properties i was unable to encode a table due to some number based key properties. i modified the encode function to check for number based keys in addition to the already present string based keys. an error will still be returned if the key is neither string nor number based. it works in my cases Please see this link: https://github.com/rxi/json.lua/pull/43#issue-1446161604 --- json.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/json.lua b/json.lua index f65c88d..dd7c8a9 100644 --- a/json.lua +++ b/json.lua @@ -90,20 +90,22 @@ local function encode_table(val, stack) end -- Encode for i=1,length do - table.insert(res, encode(val[i], stack)) + res[#res + 1] = encode(val[i], stack) end stack[val] = nil return "[" .. table.concat(res, ",") .. "]" else -- Treat as an object for k, v in pairs(val) do - --[[ - if type(k) ~= "string" then - error("invalid table: mixed or invalid key types") + if type(k) == "string" then + res[#res + 1] = encode(k, stack) .. ":" .. encode(v, stack) + elseif type(k) == "number" then + res[#res + 1] = encode(string.format(k), stack) .. ":" .. encode(v, stack) + else + error("invalid table: mixed or invalid key types"); end - ]] if k ~= "_" then - table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + res[#res + 1] = encode(k, stack) .. ":" .. encode(v, stack) end end stack[val] = nil From 176068b42362aa98b9d20e60fb8d20de0dbafea8 Mon Sep 17 00:00:00 2001 From: alexandro-rezakhani <85354050+alexandro-rezakhani@users.noreply.github.com> Date: Wed, 23 Nov 2022 03:16:20 -0500 Subject: [PATCH 12/12] Finalized After testing, I found some minor issues with some of the merged code. --- json.lua | 96 ++++++++++++++++++++++++++------------------------------ 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/json.lua b/json.lua index dd7c8a9..372e69e 100644 --- a/json.lua +++ b/json.lua @@ -2,6 +2,7 @@ -- json.lua -- -- Copyright (c) 2020 rxi +-- Copyright (c) 2020 alexandro-rezakhani -- -- Permission is hereby granted, free of charge, to any person obtaining a copy of -- this software and associated documentation files (the "Software"), to deal in @@ -55,7 +56,6 @@ local function encode_nil(val) return "null" end -local json_object_tag = {} local function encode_table(val, stack) local res = {} @@ -65,52 +65,47 @@ local function encode_table(val, stack) if stack[val] then error("circular reference") end stack[val] = true + -- Check whether to treat as a array or object + local array = true + local length = 0 + local nLen = 0 + for k,v in pairs(val) do + if (type(k) ~= "number" or k<=0) and not (k == "n" and type(v) == "number") then + array = nil + break -- Treat as object + else + if k > length then + length = k + end + if k == "n" and type(v) == "number" then + nLen = v + end + end + end + if array then + if nLen > length then + length = nLen + end + -- Encode + for i=1,length do + table.insert(res, encode(val[i], stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" - if getmetatable(val) ~= json_object_tag and (rawget(val, 1) ~= nil or next(val) == nil) then - -- Check whether to treat as a array or object - local array = true - local length = 0 - local nLen = 0 - for k,v in pairs(val) do - if (type(k) ~= "number" or k<=0) and not (k == "n" and type(v) == "number") then - array = nil - break -- Treat as object + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) == "string" then + res[#res + 1] = encode(k, stack) .. ":" .. encode(v, stack) + elseif type(k) == "number" then + res[#res + 1] = encode(string.format(k), stack) .. ":" .. encode(v, stack) else - if k > length then - length = k - end - if k == "n" and type(v) == "number" then - nLen = v - end + error("invalid table: mixed or invalid key types"); end end - if array then - if nLen > length then - length = nLen - end - -- Encode - for i=1,length do - res[#res + 1] = encode(val[i], stack) - end - stack[val] = nil - return "[" .. table.concat(res, ",") .. "]" - else - -- Treat as an object - for k, v in pairs(val) do - if type(k) == "string" then - res[#res + 1] = encode(k, stack) .. ":" .. encode(v, stack) - elseif type(k) == "number" then - res[#res + 1] = encode(string.format(k), stack) .. ":" .. encode(v, stack) - else - error("invalid table: mixed or invalid key types"); - end - if k ~= "_" then - res[#res + 1] = encode(k, stack) .. ":" .. encode(v, stack) - end - end - stack[val] = nil - return "{" .. table.concat(res, ",") .. "}" - end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" end end @@ -176,8 +171,6 @@ local space_chars = create_set(" ", "\t", "\r", "\n") local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") local literals = create_set("true", "false", "null") -local locale_character = (string.match(tostring(1/2),'%p')) -- return point decimal or comma decimal according to the locale - local literal_map = { [ "true" ] = true, @@ -240,7 +233,7 @@ end local function parse_string(str, i) - local res = {} + local res = "" local j = i + 1 local k = j @@ -251,25 +244,25 @@ local function parse_string(str, i) decode_error(str, j, "control character in string") elseif x == 92 then -- `\`: Escape - res[#res + 1] = str:sub(k, j - 1) + res = res .. str:sub(k, j - 1) j = j + 1 local c = str:sub(j, j) if c == "u" then local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or str:match("^%x%x%x%x", j + 1) or decode_error(str, j - 1, "invalid unicode escape in string") - res[#res + 1] = parse_unicode_escape(hex) + res = res .. parse_unicode_escape(hex) j = j + #hex else if not escape_chars[c] then decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") end - res[#res + 1] = escape_char_map_inv[c] + res = res .. escape_char_map_inv[c] end k = j + 1 elseif x == 34 then -- `"`: End of string - res[#res + 1] = str:sub(k, j - 1) + res = res .. str:sub(k, j - 1) return res, j + 1 end @@ -283,13 +276,14 @@ end local function parse_number(str, i) local x = next_char(str, i, delim_chars) local s = str:sub(i, x - 1) - s = s:gsub('[%.%,]',locale_character) -- replace the comma decimal or point decimal with the correct one according to the locale local n = tonumber(s) if not n then decode_error(str, i, "invalid number '" .. s .. "'") end return n, x end + + local function parse_literal(str, i) local x = next_char(str, i, delim_chars) local word = str:sub(i, x - 1)