Fix JsonEncode treating tables with index 0 as arrays (#454)

## Summary
- Fix `Util.JsonEncode` incorrectly encoding Lua tables with index 0 as
JSON arrays
- Added `IsLuaArray()` helper function that validates all keys are
integers >= 1
- Tables with non-standard indices (like 0) are now correctly encoded as
JSON objects

  Closes #348

  ## Before
  ```lua
  Util.JsonEncode({[0]="a", [1]=true, [2]=0.1})
  -- Output: ["a",true,0.1]  (incorrect - data loss)
  ```
  ## After
  ```lua
  Util.JsonEncode({[0]="a", [1]=true, [2]=0.1})
  -- Output: {"0":"a","1":true,"2":0.1}  (correct)
  ```
This commit is contained in:
Tixx authored and GitHub committed 2026-09-09 11:42:16 +02:00
commit 7ece770eb7
1 file changed
+18 -12
+18 -12
View File
@@ -693,6 +693,22 @@ std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) {
return Result; return Result;
} }
static bool IsLuaArray(const sol::table& table) {
for (const auto& pair : table) {
if (pair.first.get_type() != sol::type::number) {
return false;
}
if (!pair.first.is<int>()) {
return false;
}
int key = pair.first.as<int>();
if (key < 1) {
return false;
}
}
return true;
}
static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, bool is_array, size_t depth = 0) { static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, bool is_array, size_t depth = 0) {
if (depth > 100) { if (depth > 100) {
beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path"); beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path");
@@ -762,12 +778,7 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c
if (right.as<sol::table>().empty()) { if (right.as<sol::table>().empty()) {
value = nlohmann::json::object(); value = nlohmann::json::object();
} else { } else {
bool local_is_array = true; bool local_is_array = IsLuaArray(right.as<sol::table>());
for (const auto& pair : right.as<sol::table>()) {
if (pair.first.get_type() != sol::type::number) {
local_is_array = false;
}
}
for (const auto& pair : right.as<sol::table>()) { for (const auto& pair : right.as<sol::table>()) {
JsonEncodeRecursive(value, pair.first, pair.second, local_is_array, depth + 1); JsonEncodeRecursive(value, pair.first, pair.second, local_is_array, depth + 1);
} }
@@ -790,12 +801,7 @@ std::string LuaAPI::MP::JsonEncode(const sol::table& object) {
if (object.as<sol::table>().empty()) { if (object.as<sol::table>().empty()) {
json = nlohmann::json::object(); json = nlohmann::json::object();
} else { } else {
bool is_array = true; bool is_array = IsLuaArray(object.as<sol::table>());
for (const auto& pair : object.as<sol::table>()) {
if (pair.first.get_type() != sol::type::number) {
is_array = false;
}
}
for (const auto& entry : object) { for (const auto& entry : object) {
JsonEncodeRecursive(json, entry.first, entry.second, is_array); JsonEncodeRecursive(json, entry.first, entry.second, is_array);
} }