diff options
| author | Thibault Charbonnier <thibaultcha@me.com> | 2016-02-28 20:03:12 -0800 |
|---|---|---|
| committer | Thibault Charbonnier <thibaultcha@me.com> | 2016-03-03 10:54:53 -0800 |
| commit | 7a9c25ee69f38974e99322971eace37ba1753074 (patch) | |
| tree | 8aded977eea3eca5f7c6ff2934615a602dacbc8f | |
| parent | 77b8669c95500629dff6e34b4b6b0df5d0041ddf (diff) | |
| download | lua-cjson-7a9c25ee69f38974e99322971eace37ba1753074.tar.gz lua-cjson-7a9c25ee69f38974e99322971eace37ba1753074.tar.bz2 lua-cjson-7a9c25ee69f38974e99322971eace37ba1753074.zip | |
feat: cjson.as_array metamethod to enforce empty array encoding
A proposed improved patch of openresty/lua-cjson#1 (a patch commonly
proposed to lua-cjson and its forks), taking into considerations
comments from the original PR.
- use a lightuserdata key to store the metatable in the Lua Registry
(more efficient and avoiding conflicts)
- provide a lightuserdata resulting in empty arrays as well
- tests cases moved to t/agentzh.t, where cases for
'encode_empty_table_as_object' are already written. It seems like a
better place for tests specific to the OpenResty fork's additions.
- a more complex test case
| -rw-r--r-- | lua_cjson.c | 37 | ||||
| -rw-r--r-- | tests/agentzh.t | 76 |
2 files changed, 108 insertions, 5 deletions
diff --git a/lua_cjson.c b/lua_cjson.c index 5ff3bf7..5f4faf2 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -75,6 +75,8 @@ | |||
| 75 | #define DEFAULT_DECODE_INVALID_NUMBERS 0 | 75 | #define DEFAULT_DECODE_INVALID_NUMBERS 0 |
| 76 | #endif | 76 | #endif |
| 77 | 77 | ||
| 78 | static const char * const *json_empty_array; | ||
| 79 | |||
| 78 | typedef enum { | 80 | typedef enum { |
| 79 | T_OBJ_BEGIN, | 81 | T_OBJ_BEGIN, |
| 80 | T_OBJ_END, | 82 | T_OBJ_END, |
| @@ -698,8 +700,21 @@ static void json_append_data(lua_State *l, json_config_t *cfg, | |||
| 698 | len = lua_array_length(l, cfg, json); | 700 | len = lua_array_length(l, cfg, json); |
| 699 | if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) | 701 | if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) |
| 700 | json_append_array(l, cfg, current_depth, json, len); | 702 | json_append_array(l, cfg, current_depth, json, len); |
| 701 | else | 703 | else { |
| 702 | json_append_object(l, cfg, current_depth, json); | 704 | int as_array = 0; |
| 705 | if (lua_getmetatable(l, -1)) { | ||
| 706 | lua_pushlightuserdata(l, &json_empty_array); | ||
| 707 | lua_rawget(l, LUA_REGISTRYINDEX); | ||
| 708 | as_array = lua_rawequal(l, -1, -2); | ||
| 709 | lua_pop(l, 2); | ||
| 710 | } | ||
| 711 | |||
| 712 | if (as_array) { | ||
| 713 | json_append_array(l, cfg, current_depth, json, 0); | ||
| 714 | } else { | ||
| 715 | json_append_object(l, cfg, current_depth, json); | ||
| 716 | } | ||
| 717 | } | ||
| 703 | break; | 718 | break; |
| 704 | case LUA_TNIL: | 719 | case LUA_TNIL: |
| 705 | strbuf_append_mem(json, "null", 4); | 720 | strbuf_append_mem(json, "null", 4); |
| @@ -707,8 +722,10 @@ static void json_append_data(lua_State *l, json_config_t *cfg, | |||
| 707 | case LUA_TLIGHTUSERDATA: | 722 | case LUA_TLIGHTUSERDATA: |
| 708 | if (lua_touserdata(l, -1) == NULL) { | 723 | if (lua_touserdata(l, -1) == NULL) { |
| 709 | strbuf_append_mem(json, "null", 4); | 724 | strbuf_append_mem(json, "null", 4); |
| 710 | break; | 725 | } else if (lua_touserdata(l, -1) == &json_empty_array) { |
| 726 | json_append_array(l, cfg, current_depth, json, 0); | ||
| 711 | } | 727 | } |
| 728 | break; | ||
| 712 | default: | 729 | default: |
| 713 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, | 730 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, |
| 714 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ | 731 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ |
| @@ -1378,6 +1395,11 @@ static int lua_cjson_new(lua_State *l) | |||
| 1378 | /* Initialise number conversions */ | 1395 | /* Initialise number conversions */ |
| 1379 | fpconv_init(); | 1396 | fpconv_init(); |
| 1380 | 1397 | ||
| 1398 | /* Create empty array metatable */ | ||
| 1399 | lua_pushlightuserdata(l, &json_empty_array); | ||
| 1400 | lua_newtable(l); | ||
| 1401 | lua_rawset(l, LUA_REGISTRYINDEX); | ||
| 1402 | |||
| 1381 | /* cjson module table */ | 1403 | /* cjson module table */ |
| 1382 | lua_newtable(l); | 1404 | lua_newtable(l); |
| 1383 | 1405 | ||
| @@ -1389,6 +1411,15 @@ static int lua_cjson_new(lua_State *l) | |||
| 1389 | lua_pushlightuserdata(l, NULL); | 1411 | lua_pushlightuserdata(l, NULL); |
| 1390 | lua_setfield(l, -2, "null"); | 1412 | lua_setfield(l, -2, "null"); |
| 1391 | 1413 | ||
| 1414 | /* Set cjson.empty_array_mt */ | ||
| 1415 | lua_pushlightuserdata(l, &json_empty_array); | ||
| 1416 | lua_rawget(l, LUA_REGISTRYINDEX); | ||
| 1417 | lua_setfield(l, -2, "empty_array_mt"); | ||
| 1418 | |||
| 1419 | /* Set cjson.empty_array */ | ||
| 1420 | lua_pushlightuserdata(l, &json_empty_array); | ||
| 1421 | lua_setfield(l, -2, "empty_array"); | ||
| 1422 | |||
| 1392 | /* Set module name / version fields */ | 1423 | /* Set module name / version fields */ |
| 1393 | lua_pushliteral(l, CJSON_MODNAME); | 1424 | lua_pushliteral(l, CJSON_MODNAME); |
| 1394 | lua_setfield(l, -2, "_NAME"); | 1425 | lua_setfield(l, -2, "_NAME"); |
diff --git a/tests/agentzh.t b/tests/agentzh.t index 0b546ff..e76f910 100644 --- a/tests/agentzh.t +++ b/tests/agentzh.t | |||
| @@ -41,7 +41,79 @@ print(cjson.encode({dogs = {}})) | |||
| 41 | 41 | ||
| 42 | 42 | ||
| 43 | 43 | ||
| 44 | === TEST 4: & in JSON | 44 | === TEST 4: empty_array userdata |
| 45 | --- lua | ||
| 46 | local cjson = require "cjson" | ||
| 47 | print(cjson.encode({arr = cjson.empty_array})) | ||
| 48 | --- out | ||
| 49 | {"arr":[]} | ||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | === TEST 5: empty_array_mt | ||
| 54 | --- lua | ||
| 55 | local cjson = require "cjson" | ||
| 56 | local empty_arr = setmetatable({}, cjson.empty_array_mt) | ||
| 57 | print(cjson.encode({arr = empty_arr})) | ||
| 58 | --- out | ||
| 59 | {"arr":[]} | ||
| 60 | |||
| 61 | |||
| 62 | |||
| 63 | === TEST 6: empty_array_mt and empty tables as objects (explicit) | ||
| 64 | --- lua | ||
| 65 | local cjson = require "cjson" | ||
| 66 | local empty_arr = setmetatable({}, cjson.empty_array_mt) | ||
| 67 | print(cjson.encode({obj = {}, arr = empty_arr})) | ||
| 68 | --- out | ||
| 69 | {"arr":[],"obj":{}} | ||
| 70 | |||
| 71 | |||
| 72 | |||
| 73 | === TEST 7: empty_array_mt and empty tables as objects (explicit) | ||
| 74 | --- lua | ||
| 75 | local cjson = require "cjson" | ||
| 76 | cjson.encode_empty_table_as_object(true) | ||
| 77 | local empty_arr = setmetatable({}, cjson.empty_array_mt) | ||
| 78 | local data = { | ||
| 79 | arr = empty_arr, | ||
| 80 | foo = { | ||
| 81 | obj = {}, | ||
| 82 | foobar = { | ||
| 83 | arr = cjson.empty_array, | ||
| 84 | obj = {} | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | print(cjson.encode(data)) | ||
| 89 | --- out | ||
| 90 | {"foo":{"foobar":{"obj":{},"arr":[]},"obj":{}},"arr":[]} | ||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | === TEST 8: empty_array_mt on non-empty tables | ||
| 95 | --- lua | ||
| 96 | local cjson = require "cjson" | ||
| 97 | cjson.encode_empty_table_as_object(true) | ||
| 98 | local array = {"hello", "world", "lua"} | ||
| 99 | setmetatable(array, cjson.empty_array_mt) | ||
| 100 | local data = { | ||
| 101 | arr = array, | ||
| 102 | foo = { | ||
| 103 | obj = {}, | ||
| 104 | foobar = { | ||
| 105 | arr = cjson.empty_array, | ||
| 106 | obj = {} | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | print(cjson.encode(data)) | ||
| 111 | --- out | ||
| 112 | {"foo":{"foobar":{"obj":{},"arr":[]},"obj":{}},"arr":["hello","world","lua"]} | ||
| 113 | |||
| 114 | |||
| 115 | |||
| 116 | === TEST 9: & in JSON | ||
| 45 | --- lua | 117 | --- lua |
| 46 | local cjson = require "cjson" | 118 | local cjson = require "cjson" |
| 47 | local a="[\"a=1&b=2\"]" | 119 | local a="[\"a=1&b=2\"]" |
| @@ -52,7 +124,7 @@ print(cjson.encode(b)) | |||
| 52 | 124 | ||
| 53 | 125 | ||
| 54 | 126 | ||
| 55 | === TEST 5: default and max precision | 127 | === TEST 10: default and max precision |
| 56 | --- lua | 128 | --- lua |
| 57 | local math = require "math" | 129 | local math = require "math" |
| 58 | local cjson = require "cjson" | 130 | local cjson = require "cjson" |
