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  

Impressum / About Us