webmcp
changeset 164:8e969519f7c7
JSON pretty printer
author | jbe |
---|---|
date | Thu Jul 31 20:49:16 2014 +0200 (2014-07-31) |
parents | 581878c613d6 |
children | 0c230f701967 |
files | libraries/json/json.c |
line diff
1.1 --- a/libraries/json/json.c Thu Jul 31 19:27:29 2014 +0200 1.2 +++ b/libraries/json/json.c Thu Jul 31 20:49:16 2014 +0200 1.3 @@ -212,7 +212,7 @@ 1.4 // limit nested levels: 1.5 if (level >= JSON_MAXDEPTH) { 1.6 lua_pushnil(L); 1.7 - lua_pushliteral(L, "Too many nested JSON levels"); 1.8 + lua_pushfstring(L, "More than %d nested JSON levels", JSON_MAXDEPTH); 1.9 return 2; 1.10 } 1.11 // additional buffer overflow protection: 1.12 @@ -711,7 +711,15 @@ 1.13 #define JSON_TABLETYPE_OBJECT 1 1.14 #define JSON_TABLETYPE_ARRAY 2 1.15 1.16 -static int json_export(lua_State *L) { 1.17 +#define json_export_internal_indentstring_idx 1 1.18 +#define json_export_internal_level_idx 2 1.19 +#define json_export_internal_value_idx 3 1.20 +#define json_export_internal_tmp_idx 4 1.21 + 1.22 +static int json_export_internal(lua_State *L) { 1.23 + int level; 1.24 + int pretty; 1.25 + int i; 1.26 lua_Number num; 1.27 const char *str; 1.28 unsigned char c; 1.29 @@ -719,34 +727,36 @@ 1.30 size_t pos = 0; 1.31 luaL_Buffer buf; 1.32 char hexcode[7]; // backslash, character 'u', 4 hex digits, and terminating NULL byte 1.33 - int luatype; 1.34 int tabletype = JSON_TABLETYPE_UNKNOWN; 1.35 - int needsep = 0; 1.36 + int anyelement = 0; 1.37 size_t keycount = 0; 1.38 size_t keypos = 0; 1.39 json_key_t *keybuf; 1.40 lua_Integer idx; 1.41 - lua_settop(L, 1); 1.42 - if (json_isnullmark(L, 1)) { 1.43 + lua_settop(L, json_export_internal_value_idx); 1.44 + if (json_isnullmark(L, json_export_internal_value_idx)) { 1.45 + lua_pop(L, 1); 1.46 lua_pushnil(L); 1.47 - lua_replace(L, 1); 1.48 } 1.49 - switch (lua_type(L, 1)) { 1.50 + switch (lua_type(L, json_export_internal_value_idx)) { 1.51 case LUA_TNIL: 1.52 lua_pushliteral(L, "null"); 1.53 return 1; 1.54 case LUA_TNUMBER: 1.55 - num = lua_tonumber(L, 1); 1.56 + num = lua_tonumber(L, json_export_internal_value_idx); 1.57 if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value"); 1.58 if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers"); 1.59 - lua_tostring(L, 1); 1.60 + lua_tostring(L, json_export_internal_value_idx); 1.61 return 1; 1.62 case LUA_TBOOLEAN: 1.63 - if (lua_toboolean(L, 1)) lua_pushliteral(L, "true"); 1.64 - else lua_pushliteral(L, "false"); 1.65 + if (lua_toboolean(L, json_export_internal_value_idx)) { 1.66 + lua_pushliteral(L, "true"); 1.67 + } else { 1.68 + lua_pushliteral(L, "false"); 1.69 + } 1.70 return 1; 1.71 case LUA_TSTRING: 1.72 - str = lua_tolstring(L, 1, &strlen); 1.73 + str = lua_tolstring(L, 3, &strlen); 1.74 luaL_buffinit(L, &buf); 1.75 luaL_addchar(&buf, '"'); 1.76 while (pos < strlen) { 1.77 @@ -770,43 +780,55 @@ 1.78 luaL_pushresult(&buf); 1.79 return 1; 1.80 case LUA_TTABLE: 1.81 - if (lua_getmetatable(L, 1)) { 1.82 + if (lua_getmetatable(L, json_export_internal_value_idx)) { 1.83 json_regfetch(L, objectmt); 1.84 if (lua_rawequal(L, -2, -1)) { 1.85 tabletype = JSON_TABLETYPE_OBJECT; 1.86 } else { 1.87 json_regfetch(L, arraymt); 1.88 - if (lua_rawequal(L, -3, -1)) tabletype = JSON_TABLETYPE_ARRAY; 1.89 + if (lua_rawequal(L, -3, -1)) { 1.90 + tabletype = JSON_TABLETYPE_ARRAY; 1.91 + } else { 1.92 + return luaL_error(L, "JSON export not possible for tables with nonsupported metatable"); 1.93 + } 1.94 } 1.95 } 1.96 json_regfetch(L, shadowtbl); 1.97 - lua_pushvalue(L, 1); 1.98 + lua_pushvalue(L, json_export_internal_value_idx); 1.99 lua_rawget(L, -2); 1.100 - if (!lua_isnil(L, -1)) lua_replace(L, 1); 1.101 - lua_settop(L, 1); 1.102 + if (!lua_isnil(L, -1)) lua_replace(L, json_export_internal_value_idx); 1.103 + lua_settop(L, json_export_internal_value_idx); 1.104 if (tabletype == JSON_TABLETYPE_UNKNOWN) { 1.105 - for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { 1.106 - luatype = lua_type(L, -2); 1.107 - if (tabletype == JSON_TABLETYPE_UNKNOWN) { 1.108 - if (luatype == LUA_TSTRING) tabletype = JSON_TABLETYPE_OBJECT; 1.109 - else if (luatype == LUA_TNUMBER) tabletype = JSON_TABLETYPE_ARRAY; 1.110 - } else if ( 1.111 - (tabletype == JSON_TABLETYPE_OBJECT && luatype == LUA_TNUMBER) || 1.112 - (tabletype == JSON_TABLETYPE_ARRAY && luatype == LUA_TSTRING) 1.113 - ) { 1.114 - goto json_export_tabletype_error; 1.115 + for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { 1.116 + switch (lua_type(L, -2)) { 1.117 + case LUA_TSTRING: 1.118 + keycount++; 1.119 + if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT; 1.120 + else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error; 1.121 + break; 1.122 + case LUA_TNUMBER: 1.123 + if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY; 1.124 + else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error; 1.125 + break; 1.126 } 1.127 } 1.128 } 1.129 + pretty = lua_toboolean(L, json_export_internal_indentstring_idx); 1.130 + level = lua_tointeger(L, json_export_internal_level_idx) + 1; 1.131 + if (level > JSON_MAXDEPTH) { 1.132 + return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH); 1.133 + } 1.134 switch (tabletype) { 1.135 case JSON_TABLETYPE_OBJECT: 1.136 - for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { 1.137 - if (lua_type(L, -2) == LUA_TSTRING) keycount++; 1.138 + if (!keycount) { 1.139 + for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { 1.140 + if (lua_type(L, -2) == LUA_TSTRING) keycount++; 1.141 + } 1.142 } 1.143 if (keycount) { 1.144 keybuf = calloc(keycount, sizeof(json_key_t)); 1.145 if (!keybuf) return luaL_error(L, "Memory allocation failed in JSON library"); 1.146 - for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { 1.147 + for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { 1.148 if (lua_type(L, -2) == LUA_TSTRING) { 1.149 json_key_t *key = keybuf + (keypos++); 1.150 key->data = lua_tolstring(L, -2, &key->length); 1.151 @@ -819,46 +841,83 @@ 1.152 for (keypos=0; keypos<keycount; keypos++) { 1.153 json_key_t *key = keybuf + keypos; 1.154 if (keypos) luaL_addchar(&buf, ','); 1.155 - lua_pushcfunction(L, json_export); 1.156 + if (pretty) { 1.157 + luaL_addchar(&buf, '\n'); 1.158 + for (i=0; i<level; i++) { 1.159 + lua_pushvalue(L, json_export_internal_indentstring_idx); 1.160 + luaL_addvalue(&buf); 1.161 + } 1.162 + } 1.163 + lua_pushcfunction(L, json_export_internal); 1.164 + lua_pushvalue(L, json_export_internal_indentstring_idx); 1.165 + lua_pushinteger(L, level); 1.166 lua_pushlstring(L, key->data, key->length); 1.167 - if (lua_pcall(L, 1, 1, 0)) { 1.168 + if (lua_pcall(L, 3, 1, 0)) { 1.169 if (keybuf) free(keybuf); 1.170 return lua_error(L); 1.171 } 1.172 luaL_addvalue(&buf); 1.173 luaL_addchar(&buf, ':'); 1.174 - lua_pushcfunction(L, json_export); 1.175 + if (pretty) luaL_addchar(&buf, ' '); 1.176 + lua_pushcfunction(L, json_export_internal); 1.177 + lua_pushvalue(L, json_export_internal_indentstring_idx); 1.178 + lua_pushinteger(L, level); 1.179 lua_pushlstring(L, key->data, key->length); 1.180 - lua_rawget(L, 1); 1.181 - if (lua_pcall(L, 1, 1, 0)) { 1.182 + lua_rawget(L, json_export_internal_value_idx); 1.183 + if (lua_pcall(L, 3, 1, 0)) { 1.184 if (keybuf) free(keybuf); 1.185 return lua_error(L); 1.186 } 1.187 luaL_addvalue(&buf); 1.188 } 1.189 if (keybuf) free(keybuf); 1.190 + if (pretty && keycount != 0) { 1.191 + luaL_addchar(&buf, '\n'); 1.192 + for (i=0; i<level-1; i++) { 1.193 + lua_pushvalue(L, json_export_internal_indentstring_idx); 1.194 + luaL_addvalue(&buf); 1.195 + } 1.196 + } 1.197 luaL_addchar(&buf, '}'); 1.198 + if (pretty && level == 1) luaL_addchar(&buf, '\n'); 1.199 luaL_pushresult(&buf); 1.200 return 1; 1.201 case JSON_TABLETYPE_ARRAY: 1.202 - lua_settop(L, 2); 1.203 + lua_settop(L, json_export_internal_tmp_idx); 1.204 luaL_buffinit(L, &buf); 1.205 luaL_addchar(&buf, '['); 1.206 for (idx = 1; ; idx++) { 1.207 - lua_rawgeti(L, 1, idx); 1.208 + lua_rawgeti(L, json_export_internal_value_idx, idx); 1.209 if (lua_isnil(L, -1)) { 1.210 lua_pop(L, 1); 1.211 break; 1.212 } 1.213 - lua_replace(L, 2); 1.214 - if (needsep) luaL_addchar(&buf, ','); 1.215 - else needsep = 1; 1.216 - lua_pushcfunction(L, json_export); 1.217 - lua_pushvalue(L, 2); 1.218 - lua_call(L, 1, 1); 1.219 + lua_replace(L, json_export_internal_tmp_idx); 1.220 + if (anyelement) luaL_addchar(&buf, ','); 1.221 + anyelement = 1; 1.222 + if (pretty) { 1.223 + luaL_addchar(&buf, '\n'); 1.224 + for (i=0; i<level; i++) { 1.225 + lua_pushvalue(L, json_export_internal_indentstring_idx); 1.226 + luaL_addvalue(&buf); 1.227 + } 1.228 + } 1.229 + lua_pushcfunction(L, json_export_internal); 1.230 + lua_pushvalue(L, json_export_internal_indentstring_idx); 1.231 + lua_pushinteger(L, level); 1.232 + lua_pushvalue(L, json_export_internal_tmp_idx); 1.233 + lua_call(L, 3, 1); 1.234 luaL_addvalue(&buf); 1.235 } 1.236 + if (pretty && anyelement) { 1.237 + luaL_addchar(&buf, '\n'); 1.238 + for (i=0; i<level-1; i++) { 1.239 + lua_pushvalue(L, json_export_internal_indentstring_idx); 1.240 + luaL_addvalue(&buf); 1.241 + } 1.242 + } 1.243 luaL_addchar(&buf, ']'); 1.244 + if (pretty && level == 1) luaL_addchar(&buf, '\n'); 1.245 luaL_pushresult(&buf); 1.246 return 1; 1.247 } 1.248 @@ -868,12 +927,34 @@ 1.249 return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, 1))); 1.250 } 1.251 1.252 +static int json_export(lua_State *L) { 1.253 + lua_settop(L, 1); 1.254 + lua_pushcfunction(L, json_export_internal); 1.255 + lua_pushnil(L); 1.256 + lua_pushinteger(L, 0); 1.257 + lua_pushvalue(L, 1); 1.258 + lua_call(L, 3, 1); 1.259 + return 1; 1.260 +} 1.261 + 1.262 +static int json_pretty(lua_State *L) { 1.263 + lua_settop(L, 2); 1.264 + lua_pushcfunction(L, json_export_internal); 1.265 + if (lua_isnil(L, 2)) lua_pushliteral(L, " "); 1.266 + else lua_pushvalue(L, 2); 1.267 + lua_pushinteger(L, 0); 1.268 + lua_pushvalue(L, 1); 1.269 + lua_call(L, 3, 1); 1.270 + return 1; 1.271 +} 1.272 + 1.273 // functions in library module: 1.274 static const struct luaL_Reg json_module_functions[] = { 1.275 {"object", json_object}, 1.276 {"array", json_array}, 1.277 {"import", json_import}, 1.278 {"export", json_export}, 1.279 + {"pretty", json_pretty}, 1.280 {"get", json_get}, 1.281 {"type", json_type}, 1.282 {NULL, NULL}