From 1ed90c90c304c6cc9cfddb722e4d15a1221d0177 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Wed, 18 Dec 2013 16:46:53 -0500 Subject: [PATCH] Add 'minetest.write_json' --- doc/lua_api.txt | 11 +++++++- src/script/common/c_content.cpp | 49 +++++++++++++++++++++++++++++++++ src/script/common/c_content.h | 3 ++ src/script/lua_api/l_util.cpp | 27 ++++++++++++++++++ src/script/lua_api/l_util.h | 3 ++ 5 files changed, 92 insertions(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index abfb8f94b..135e2cfcd 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1530,7 +1530,16 @@ minetest.parse_json(string[, nullvalue]) -> something ^ nullvalue: returned in place of the JSON null; defaults to nil ^ On success returns a table, a string, a number, a boolean or nullvalue ^ On failure outputs an error message and returns nil -^ Example: parse_json("[10, {\"a\":false}]") -> {[1] = 10, [2] = {a = false}} +^ Example: parse_json("[10, {\"a\":false}]") -> {10, {a = false}} +minetest.write_json(data[, styled]) -> string +^ Convert a Lua table into a JSON string +^ styled: Outputs in a human-readable format if this is set, defaults to false +^ Un-serializable things like functions and userdata are saved as null. +^ Warning: JSON is more strict than the Lua table format. + 1. You can only use strings and positive integers of at least one as keys. + 2. You can not mix string and integer keys. + This is due to the fact that Javascript has two distinct array and object values. +^ Example: write_json({10, {a = false}}) -> "[10, {\"a\": false}]" minetest.serialize(table) -> string ^ Convert a table containing tables, strings, numbers, booleans and nils into string form readable by minetest.deserialize diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index cf9f28d30..8eb57ba41 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1081,3 +1081,52 @@ bool push_json_value(lua_State *L, const Json::Value &value, int nullindex) else return false; } + +// Converts Lua table --> JSON +void get_json_value(lua_State *L, Json::Value &root, int index) +{ + int type = lua_type(L, index); + if (type == LUA_TBOOLEAN) { + root = (bool) lua_toboolean(L, index); + } else if (type == LUA_TNUMBER) { + root = lua_tonumber(L, index); + } else if (type == LUA_TSTRING) { + size_t len; + const char *str = lua_tolstring(L, index, &len); + root = std::string(str, len); + } else if (type == LUA_TTABLE) { + lua_pushnil(L); + while (lua_next(L, index)) { + // Key is at -2 and value is at -1 + Json::Value value; + get_json_value(L, value, lua_gettop(L)); + + Json::ValueType roottype = root.type(); + int keytype = lua_type(L, -1); + if (keytype == LUA_TNUMBER) { + lua_Number key = lua_tonumber(L, -1); + if (roottype != Json::nullValue && roottype != Json::arrayValue) { + throw LuaError(NULL, "Can't mix array and object values in JSON"); + } else if (key < 1) { + throw LuaError(NULL, "Can't use zero-based or negative indexes in JSON"); + } else if (floor(key) != key) { + throw LuaError(NULL, "Can't use indexes with a fractional part in JSON"); + } + root[(Json::ArrayIndex) key - 1] = value; + } else if (keytype == LUA_TSTRING) { + if (roottype != Json::nullValue && roottype != Json::objectValue) { + throw LuaError(NULL, "Can't mix array and object values in JSON"); + } + root[lua_tostring(L, -1)] = value; + } else { + throw LuaError(NULL, "Lua key to convert to JSON is not a string or number"); + } + } + } else if (type == LUA_TNIL) { + root = Json::nullValue; + } else { + throw LuaError(NULL, "Can only store booleans, numbers, strings, objects, arrays, and null in JSON"); + } + lua_pop(L, 1); // Pop value +} + diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 27019e29e..3b85e5403 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -150,6 +150,9 @@ void luaentity_get (lua_State *L,u16 id); bool push_json_value (lua_State *L, const Json::Value &value, int nullindex); +void get_json_value (lua_State *L, + Json::Value &root, + int index); extern struct EnumString es_TileAnimationType[]; diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index fe10e4f57..9fa6fcb77 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -179,6 +179,32 @@ int ModApiUtil::l_parse_json(lua_State *L) return 1; } +// write_json(data[, styled]) -> string +int ModApiUtil::l_write_json(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + bool styled = false; + if (!lua_isnone(L, 2)) { + styled = lua_toboolean(L, 2); + lua_pop(L, 1); + } + + Json::Value root; + get_json_value(L, root, 1); + + std::string out; + if (styled) { + Json::StyledWriter writer; + out = writer.write(root); + } else { + Json::FastWriter writer; + out = writer.write(root); + } + lua_pushlstring(L, out.c_str(), out.size()); + return 1; +} + // get_dig_params(groups, tool_capabilities[, time_from_last_punch]) int ModApiUtil::l_get_dig_params(lua_State *L) { @@ -249,6 +275,7 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(setting_save); API_FCT(parse_json); + API_FCT(write_json); API_FCT(get_dig_params); API_FCT(get_hit_params); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index d91c880cf..13357587a 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -64,6 +64,9 @@ private: // parse_json(str[, nullvalue]) static int l_parse_json(lua_State *L); + // write_json(data[, styled]) + static int l_write_json(lua_State *L); + // get_dig_params(groups, tool_capabilities[, time_from_last_punch]) static int l_get_dig_params(lua_State *L);