Skip to content

Instantly share code, notes, and snippets.

@kahrl
Created September 1, 2013 23:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kahrl/6407927 to your computer and use it in GitHub Desktop.
Save kahrl/6407927 to your computer and use it in GitHub Desktop.
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 94397e1..1d2f295 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -1471,6 +1471,12 @@ minetest.get_content_id(name) -> integer
^ Gets the internal content ID of name
minetest.get_name_from_content_id(content_id) -> string
^ Gets the name of the content with that content ID
+minetest.parse_json(string[, nullvalue]) -> something
+^ Convert a string containing JSON data into the Lua equivalent
+^ 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}}
minetest.serialize(table) -> string
^ Convert a table containing tables, strings, numbers, booleans and nils
into string form readable by minetest.deserialize
diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt
index 10524f4..40a2437 100644
--- a/doc/menu_lua_api.txt
+++ b/doc/menu_lua_api.txt
@@ -188,6 +188,12 @@ engine.gettext(string) -> string
fgettext(string, ...) -> string
^ call engine.gettext(string), replace "$1"..."$9" with the given
^ extra arguments, call engine.formspec_escape and return the result
+engine.parse_json(string[, nullvalue]) -> something
+^ Convert a string containing JSON data into the Lua equivalent
+^ 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}}
dump(obj, dumped={})
^ Return object serialized as a string
string:split(separator)
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index 2e26adb..6f80cb2 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -31,6 +31,7 @@
#include "tool.h"
#include "serverobject.h"
#include "mapgen.h"
+#include "json/json.h"
struct EnumString es_TileAnimationType[] =
{
@@ -998,3 +999,86 @@ bool read_schematic(lua_State *L, int index, DecoSchematic *dschem, Server *serv
return true;
}
+
+/******************************************************************************/
+// Returns actual depth of json value tree, or -1 if exceeding maxdepth
+static int push_json_value_checkdepth(const Json::Value &value, int maxdepth)
+{
+ if (maxdepth <= 0)
+ return -1;
+ if (!value.isArray() && !value.isObject())
+ return 1;
+
+ int objdepth = 0;
+ for (Json::Value::const_iterator it = value.begin();
+ it != value.end(); ++it) {
+ int elemdepth = push_json_value_checkdepth(*it, maxdepth - 1);
+ if (elemdepth < 0)
+ return -1;
+ if (elemdepth > objdepth)
+ objdepth = elemdepth;
+ }
+ return objdepth + 1;
+}
+// Recursive function to convert JSON --> Lua table
+static void push_json_value_helper(lua_State *L, const Json::Value &value,
+ int nullindex)
+{
+ switch(value.type()) {
+ case Json::nullValue:
+ default:
+ lua_pushvalue(L, nullindex);
+ break;
+ case Json::intValue:
+ lua_pushinteger(L, value.asInt());
+ break;
+ case Json::uintValue:
+ lua_pushinteger(L, value.asUInt());
+ break;
+ case Json::realValue:
+ lua_pushnumber(L, value.asDouble());
+ break;
+ case Json::stringValue:
+ {
+ const char *str = value.asCString();
+ lua_pushstring(L, str ? str : "");
+ }
+ break;
+ case Json::booleanValue:
+ lua_pushboolean(L, value.asInt());
+ break;
+ case Json::arrayValue:
+ lua_newtable(L);
+ for (Json::Value::const_iterator it = value.begin();
+ it != value.end(); ++it) {
+ push_json_value_helper(L, *it, nullindex);
+ lua_rawseti(L, -2, it.index() + 1);
+ }
+ break;
+ case Json::objectValue:
+ lua_newtable(L);
+ for (Json::Value::const_iterator it = value.begin();
+ it != value.end(); ++it) {
+ const char *str = it.memberName();
+ lua_pushstring(L, str ? str : "");
+ push_json_value_helper(L, *it, nullindex);
+ lua_rawset(L, -3);
+ }
+ break;
+ }
+}
+// converts JSON --> Lua table; returns false if maxdepth or stack exceeded
+// nullindex: Lua stack index of value to use in place of JSON null
+bool push_json_value(lua_State *L, const Json::Value &value,
+ int nullindex, int maxdepth)
+{
+ if(nullindex < 0)
+ nullindex = lua_gettop(L) + 1 + nullindex;
+ int depth = push_json_value_checkdepth(value, maxdepth);
+ if (depth >= 0 && lua_checkstack(L, depth*2)) {
+ push_json_value_helper(L, value, nullindex);
+ return true;
+ }
+ else
+ return false;
+}
diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h
index 6d1dfe1..24d9e53 100644
--- a/src/script/common/c_content.h
+++ b/src/script/common/c_content.h
@@ -39,6 +39,8 @@
#include "irrlichttypes_bloated.h"
#include "util/string.h"
+namespace Json { class Value; }
+
struct MapNode;
class INodeDefManager;
struct PointedThing;
@@ -145,6 +147,11 @@ bool read_schematic (lua_State *L, int index,
void luaentity_get (lua_State *L,u16 id);
+bool push_json_value (lua_State *L,
+ const Json::Value &value,
+ int nullindex,
+ int maxdepth);
+
extern struct EnumString es_TileAnimationType[];
#endif /* C_CONTENT_H_ */
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index 40a2ff4..c6be024 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -26,6 +26,7 @@
#include "tool.h"
#include "settings.h"
#include "main.h" //required for g_settings, g_settings_path
+#include "json/json.h"
// debug(...)
// Writes a line to dstream
@@ -138,6 +139,46 @@ int ModApiUtil::l_setting_save(lua_State *L)
return 0;
}
+// parse_json(str[, nullvalue])
+int ModApiUtil::l_parse_json(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ const char *jsonstr = luaL_checkstring(L, 1);
+
+ // Use passed nullvalue or default to nil
+ int nullindex = 2;
+ if (lua_isnone(L, nullindex)) {
+ lua_pushnil(L);
+ nullindex = lua_gettop(L);
+ }
+
+ Json::Value root;
+
+ {
+ Json::Reader reader;
+ std::istringstream stream(jsonstr);
+
+ if (!reader.parse(stream, root)) {
+ errorstream << "Failed to parse json data "
+ << reader.getFormattedErrorMessages();
+ errorstream << "data: \"" << jsonstr << "\""
+ << std::endl;
+ lua_pushnil(L);
+ return 1;
+ }
+ }
+
+ int maxdepth = 42;
+ if (!push_json_value(L, root, nullindex, maxdepth)) {
+ errorstream << "Failed to parse json data, "
+ << "depth limit exceeded" << std::endl;
+ errorstream << "data: \"" << jsonstr << "\"" << std::endl;
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
int ModApiUtil::l_get_dig_params(lua_State *L)
{
@@ -211,6 +252,8 @@ void ModApiUtil::Initialize(lua_State *L, int top)
API_FCT(setting_getbool);
API_FCT(setting_save);
+ API_FCT(parse_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 ac840c4..07d4c29 100644
--- a/src/script/lua_api/l_util.h
+++ b/src/script/lua_api/l_util.h
@@ -59,6 +59,9 @@ class ModApiUtil : public ModApiBase {
// setting_save()
static int l_setting_save(lua_State *L);
+ // parse_json(str[, nullvalue])
+ static int l_parse_json(lua_State *L);
+
// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
static int l_get_dig_params(lua_State *L);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment