webmcp
changeset 173:f417c4b607ed
Added json.set function to set deep values in a JSON document
author | jbe |
---|---|
date | Fri Aug 01 18:52:25 2014 +0200 (2014-08-01) |
parents | a83164390355 |
children | 070edea2a92f |
files | libraries/json/json.c |
line diff
1.1 --- a/libraries/json/json.c Fri Aug 01 17:11:59 2014 +0200 1.2 +++ b/libraries/json/json.c Fri Aug 01 18:52:25 2014 +0200 1.3 @@ -596,6 +596,8 @@ 1.4 static int json_path(lua_State *L, int type_mode) { 1.5 int stacktop; // stack index of top of stack (after shifting) 1.6 int idx = 2 + json_path_idxshift; // stack index of current argument to process 1.7 + // require at least one argument: 1.8 + luaL_checkany(L, 1); 1.9 // insert shadowtbl into stack at position 1 (shifting the arguments): 1.10 json_regfetch(L, shadowtbl); 1.11 lua_insert(L, 1); 1.12 @@ -686,6 +688,129 @@ 1.13 return json_path(L, 1); 1.14 } 1.15 1.16 +// special Lua stack indicies for json_set function: 1.17 +#define json_set_shadowtbl_idx 1 1.18 +#define json_set_objectmt_idx 2 1.19 +#define json_set_arraymt_idx 3 1.20 + 1.21 +// stack offset of arguments to json_set function: 1.22 +#define json_set_idxshift 3 1.23 + 1.24 +// sets a value (passed as second argument) in a JSON document (passed as first argument) 1.25 +// using a path (variable number of keys starting at third argument): 1.26 +static int json_set(lua_State *L) { 1.27 + int stacktop; // stack index of top of stack (after shifting) 1.28 + int idx = 3; // stack index of current argument to process 1.29 + // require at least two arguments: 1.30 + luaL_checkany(L, 1); 1.31 + luaL_checkany(L, 2); 1.32 + // insert shadowtbl into stack at position 1 (shifting the arguments): 1.33 + json_regfetch(L, shadowtbl); 1.34 + lua_insert(L, 1); 1.35 + // insert objectmt into stack at position 2 (shifting the arguments): 1.36 + json_regfetch(L, objectmt); 1.37 + lua_insert(L, 2); 1.38 + // insert arraymt into stack at position 3 (shifting the arguments): 1.39 + json_regfetch(L, arraymt); 1.40 + lua_insert(L, 3); 1.41 + // store stack index of top of stack: 1.42 + stacktop = lua_gettop(L); 1.43 + // use nil as initial "parent value": 1.44 + lua_pushnil(L); 1.45 + // use first argument as "current value": 1.46 + lua_pushvalue(L, 1 + json_set_idxshift); 1.47 + // set all necessary values in path: 1.48 + for (idx = 3 + json_set_idxshift; idx<=stacktop; idx++) { 1.49 + // push metatable of "current value" onto stack: 1.50 + if (!lua_getmetatable(L, -1)) lua_pushnil(L); 1.51 + // distinguish according to type of path key: 1.52 + switch (lua_type(L, idx)) { 1.53 + case LUA_TSTRING: 1.54 + // if path key is a string, 1.55 + // check if "current value" is a JSON object (or table without metatable): 1.56 + if ( 1.57 + lua_rawequal(L, -1, json_set_objectmt_idx) || 1.58 + (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE) 1.59 + ) { 1.60 + // if "current value" is acceptable, 1.61 + // pop metatable and leave "current value" on top of stack: 1.62 + lua_pop(L, 1); 1.63 + } else { 1.64 + // if "current value" is not acceptable: 1.65 + // pop metatable and "current value": 1.66 + lua_pop(L, 2); 1.67 + // throw error if parent element does not exist: 1.68 + if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON object"); 1.69 + // push new JSON object as "current value" onto stack: 1.70 + lua_newtable(L); 1.71 + // create and register shadow table: 1.72 + lua_pushvalue(L, -1); 1.73 + lua_newtable(L); 1.74 + lua_rawset(L, json_set_shadowtbl_idx); 1.75 + // set metatable of JSON object: 1.76 + lua_pushvalue(L, json_set_objectmt_idx); 1.77 + lua_setmetatable(L, -2); 1.78 + // set entry in "parent value": 1.79 + lua_pushvalue(L, idx-1); 1.80 + lua_pushvalue(L, -2); 1.81 + lua_settable(L, -4); 1.82 + } 1.83 + break; 1.84 + case LUA_TNUMBER: 1.85 + // if path key is a number, 1.86 + // check if "current value" is a JSON array (or table without metatable): 1.87 + if ( 1.88 + lua_rawequal(L, -1, json_set_arraymt_idx) || 1.89 + (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE) 1.90 + ) { 1.91 + // if "current value" is acceptable, 1.92 + // pop metatable and leave "current value" on top of stack: 1.93 + lua_pop(L, 1); 1.94 + } else { 1.95 + // if "current value" is not acceptable: 1.96 + // pop metatable and "current value": 1.97 + lua_pop(L, 2); 1.98 + // throw error if parent element does not exist: 1.99 + if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON array"); 1.100 + // push new JSON array as "current value" onto stack: 1.101 + lua_newtable(L); 1.102 + // create and register shadow table: 1.103 + lua_pushvalue(L, -1); 1.104 + lua_newtable(L); 1.105 + lua_rawset(L, json_set_shadowtbl_idx); 1.106 + // set metatable of JSON array: 1.107 + lua_pushvalue(L, json_set_arraymt_idx); 1.108 + lua_setmetatable(L, -2); 1.109 + // set entry in "parent value": 1.110 + lua_pushvalue(L, idx-1); 1.111 + lua_pushvalue(L, -2); 1.112 + lua_settable(L, -4); 1.113 + } 1.114 + break; 1.115 + default: 1.116 + return luaL_error(L, "Invalid path key of type %s", lua_typename(L, lua_type(L, idx))); 1.117 + } 1.118 + // check if last path element is being processed: 1.119 + if (idx == stacktop) { 1.120 + // if the last path element is being processed, 1.121 + // set last path value in "current value" container: 1.122 + lua_pushvalue(L, idx); 1.123 + lua_pushvalue(L, 2 + json_set_idxshift); 1.124 + lua_settable(L, -3); 1.125 + } else { 1.126 + // if the processed path element is not the last, 1.127 + // use old "current value" as new "parent value" 1.128 + lua_remove(L, -2); 1.129 + // push new "current value" onto stack by performing a lookup: 1.130 + lua_pushvalue(L, idx); 1.131 + lua_gettable(L, -2); 1.132 + } 1.133 + } 1.134 + // return first argument for convenience: 1.135 + lua_settop(L, 1 + json_set_idxshift); 1.136 + return 1; 1.137 +} 1.138 + 1.139 // returns the length of a JSON array (or zero for a table without numeric keys): 1.140 static int json_len(lua_State *L) { 1.141 // stack shall contain one function argument: 1.142 @@ -1079,12 +1204,13 @@ 1.143 // functions in library module: 1.144 static const struct luaL_Reg json_module_functions[] = { 1.145 {"object", json_object}, 1.146 - {"array", json_array}, 1.147 + {"array", json_array}, 1.148 {"import", json_import}, 1.149 {"export", json_export}, 1.150 {"pretty", json_pretty}, 1.151 - {"get", json_get}, 1.152 - {"type", json_type}, 1.153 + {"get", json_get}, 1.154 + {"type", json_type}, 1.155 + {"set", json_set}, 1.156 {NULL, NULL} 1.157 }; 1.158