Skip to content

Instantly share code, notes, and snippets.

@kahrl
Created September 1, 2013 23:41
Show Gist options
  • Save kahrl/6408049 to your computer and use it in GitHub Desktop.
Save kahrl/6408049 to your computer and use it in GitHub Desktop.
jsonstr = '[1, 2, 3, 4, 5, 6]'
json = engine.parse_json(jsonstr)
dump2(json)
push_json_value gettop=2 depth=2
pushed array newtable, gettop=3
pushed int 1, gettop=4
pushed int 2, gettop=4
pushed int 3, gettop=4
pushed int 4, gettop=4
pushed int 5, gettop=4
pushed int 6, gettop=4
_ = {}
_[1] = 1
_[2] = 2
_[3] = 3
_[4] = 4
_[5] = 5
_[6] = 6
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..bb94d5f 100644
--- a/doc/menu_lua_api.txt
+++ b/doc/menu_lua_api.txt
@@ -188,6 +188,8 @@ 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
+^ see minetest.parse_json (lua_api.txt)
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..5f1beaa 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,88 @@ bool read_schematic(lua_State *L, int index, DecoSchematic *dschem, Server *serv
return true;
}
+
+/******************************************************************************/
+// Returns depth of json value tree
+static int push_json_value_getdepth(const Json::Value &value)
+{
+ if (!value.isArray() && !value.isObject())
+ return 1;
+
+ int maxdepth = 0;
+ for (Json::Value::const_iterator it = value.begin();
+ it != value.end(); ++it) {
+ int elemdepth = push_json_value_getdepth(*it);
+ if (elemdepth > maxdepth)
+ maxdepth = elemdepth;
+ }
+ return maxdepth + 1;
+}
+// Recursive function to convert JSON --> Lua table
+static bool 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());
+ dstream<<"pushed int "<<value.asInt()<<", gettop="<<lua_gettop(L)<<std::endl;
+ break;
+ case Json::uintValue:
+ lua_pushinteger(L, value.asUInt());
+ dstream<<"pushed uint "<<value.asUInt()<<", gettop="<<lua_gettop(L)<<std::endl;
+ 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);
+ dstream<<"pushed array newtable, gettop="<<lua_gettop(L)<<std::endl;
+ 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;
+ }
+ return true;
+}
+// converts JSON --> Lua table; returns false if lua stack limit 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)
+{
+ if(nullindex < 0)
+ nullindex = lua_gettop(L) + 1 + nullindex;
+
+ int depth = push_json_value_getdepth(value);
+ dstream<<"push_json_value gettop="<<lua_gettop(L)<<" depth="<<depth<<std::endl;
+
+ // The maximum number of Lua stack slots used at each recursion level
+ // of push_json_value_helper is 2, so make sure there a depth * 2 slots
+ if (lua_checkstack(L, depth * 2))
+ return push_json_value_helper(L, value, nullindex);
+ else
+ return false;
+}
diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h
index 6d1dfe1..27019e2 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,10 @@ 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);
+
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..1e44ba5 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,45 @@ 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;
+ }
+ }
+
+ if (!push_json_value(L, root, nullindex)) {
+ errorstream << "Failed to parse json data, "
+ << "depth exceeds lua stack limit" << 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 +251,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