diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index a395f64..70946fa 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -60,7 +60,11 @@ std::string LuaAPI::LuaToString(const sol::object Value, size_t Indent, bool Quo } case sol::type::number: { std::stringstream ss; - ss << Value.as(); + if (Value.is()) { + ss << Value.as(); + } else { + ss << Value.as(); + } return ss.str(); } case sol::type::lua_nil: @@ -561,7 +565,11 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c key = left.as(); break; case sol::type::number: - key = std::to_string(left.as()); + if (left.is()) { + key = std::to_string(left.as()); + } else { + key = std::to_string(left.as()); + } break; default: beammp_assert_not_reachable(); @@ -589,21 +597,30 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c case sol::type::string: value = right.as(); break; - case sol::type::number: - value = right.as(); + case sol::type::number: { + if (right.is()) { + value = right.as(); + } else { + value = right.as(); + } break; + } case sol::type::function: beammp_lua_warn("unsure what to do with function in JsonEncode, ignoring"); return; case sol::type::table: { - bool local_is_array = true; - for (const auto& pair : right.as()) { - if (pair.first.get_type() != sol::type::number) { - local_is_array = false; + if (right.as().empty()) { + value = nlohmann::json::object(); + } else { + bool local_is_array = true; + for (const auto& pair : right.as()) { + if (pair.first.get_type() != sol::type::number) { + local_is_array = false; + } + } + for (const auto& pair : right.as()) { + JsonEncodeRecursive(value, pair.first, pair.second, local_is_array, depth + 1); } - } - for (const auto& pair : right.as()) { - JsonEncodeRecursive(value, pair.first, pair.second, local_is_array, depth + 1); } break; } @@ -620,14 +637,18 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c std::string LuaAPI::MP::JsonEncode(const sol::table& object) { nlohmann::json json; // table - bool is_array = true; - for (const auto& pair : object.as()) { - if (pair.first.get_type() != sol::type::number) { - is_array = false; + if (object.as().empty()) { + json = nlohmann::json::object(); + } else { + bool is_array = true; + for (const auto& pair : object.as()) { + if (pair.first.get_type() != sol::type::number) { + is_array = false; + } + } + for (const auto& entry : object) { + JsonEncodeRecursive(json, entry.first, entry.second, is_array); } - } - for (const auto& entry : object) { - JsonEncodeRecursive(json, entry.first, entry.second, is_array); } return json.dump(); } diff --git a/test/Server/JsonTests/main.lua b/test/Server/JsonTests/main.lua new file mode 100644 index 0000000..fef09b9 --- /dev/null +++ b/test/Server/JsonTests/main.lua @@ -0,0 +1,66 @@ +local function assert_eq(x, y, explain) + if x ~= y then + print("assertion '"..explain.."' failed:\n\tgot:\t", x, "\n\texpected:", y) + end +end + +---@param o1 any|table First object to compare +---@param o2 any|table Second object to compare +---@param ignore_mt boolean True to ignore metatables (a recursive function to tests tables inside tables) +function equals(o1, o2, ignore_mt) + if o1 == o2 then return true end + local o1Type = type(o1) + local o2Type = type(o2) + if o1Type ~= o2Type then return false end + if o1Type ~= 'table' then return false end + + if not ignore_mt then + local mt1 = getmetatable(o1) + if mt1 and mt1.__eq then + --compare using built in method + return o1 == o2 + end + end + + local keySet = {} + + for key1, value1 in pairs(o1) do + local value2 = o2[key1] + if value2 == nil or equals(value1, value2, ignore_mt) == false then + return false + end + keySet[key1] = true + end + + for key2, _ in pairs(o2) do + if not keySet[key2] then return false end + end + return true +end + + +local function assert_table_eq(x, y, explain) + if not equals(x, y, true) then + print("assertion '"..explain.."' failed:\n\tgot:\t", x, "\n\texpected:", y) + end +end + +assert_eq(Util.JsonEncode({1, 2, 3, 4, 5}), "[1,2,3,4,5]", "table to array") +assert_eq(Util.JsonEncode({"a", 1, 2, 3, 4, 5}), '["a",1,2,3,4,5]', "table to array") +assert_eq(Util.JsonEncode({"a", 1, 2.0, 3, 4, 5}), '["a",1,2.0,3,4,5]', "table to array") +assert_eq(Util.JsonEncode({hello="world", john={doe = 1, jane = 2.5, mike = {2, 3, 4}}, dave={}}), '{"dave":{},"hello":"world","john":{"doe":1,"jane":2.5,"mike":[2,3,4]}}', "table to obj") +assert_eq(Util.JsonEncode({a = nil}), "{}", "null obj member") +assert_eq(Util.JsonEncode({1, nil, 3}), "[1,3]", "null array member") +assert_eq(Util.JsonEncode({}), "{}", "empty array/table") +assert_eq(Util.JsonEncode({1234}), "[1234]", "int") +assert_eq(Util.JsonEncode({1234.0}), "[1234.0]", "double") + +assert_table_eq(Util.JsonDecode("[1,2,3,4,5]"), {1, 2, 3, 4, 5}, "decode table to array") +assert_table_eq(Util.JsonDecode('["a",1,2,3,4,5]'), {"a", 1, 2, 3, 4, 5}, "decode table to array") +assert_table_eq(Util.JsonDecode('["a",1,2.0,3,4,5]'), {"a", 1, 2.0, 3, 4, 5}, "decode table to array") +assert_table_eq(Util.JsonDecode('{"dave":{},"hello":"world","john":{"doe":1,"jane":2.5,"mike":[2,3,4]}}'), {hello="world", john={doe = 1, jane = 2.5, mike = {2, 3, 4}}, dave={}}, "decode table to obj") +assert_table_eq(Util.JsonDecode("{}"), {a = nil}, "decode null obj member") +assert_table_eq(Util.JsonDecode("[1,3]"), {1, 3}, "decode null array member") +assert_table_eq(Util.JsonDecode("{}"), {}, "decode empty array/table") +assert_table_eq(Util.JsonDecode("[1234]"), {1234}, "decode int") +assert_table_eq(Util.JsonDecode("[1234.0]"), {1234.0}, "decode double")