diff options
| author | Thibault Charbonnier <thibaultcha@me.com> | 2017-07-08 21:54:18 -0700 |
|---|---|---|
| committer | Yichun Zhang (agentzh) <agentzh@gmail.com> | 2017-11-15 20:41:57 -0800 |
| commit | 5f9efa4829a72935ddcd40c7da6b1a9e10939b65 (patch) | |
| tree | 319cdf13f99c753f85820c3fabb89bfa50496dae | |
| parent | a9af7965219710ac1ec1e2bbf1a63eb77be622b7 (diff) | |
| download | lua-cjson-5f9efa4829a72935ddcd40c7da6b1a9e10939b65.tar.gz lua-cjson-5f9efa4829a72935ddcd40c7da6b1a9e10939b65.tar.bz2 lua-cjson-5f9efa4829a72935ddcd40c7da6b1a9e10939b65.zip | |
feature: added new cjson.array_mt metatable to allow enforcing JSON array encoding.
Signed-off-by: Yichun Zhang (agentzh) <agentzh@gmail.com>
| -rw-r--r-- | README.md | 34 | ||||
| -rw-r--r-- | lua_cjson.c | 63 | ||||
| -rw-r--r-- | tests/agentzh.t | 79 |
3 files changed, 155 insertions, 21 deletions
| @@ -11,6 +11,7 @@ Table of Contents | |||
| 11 | * [Additions to mpx/lua](#additions) | 11 | * [Additions to mpx/lua](#additions) |
| 12 | * [encode_empty_table_as_object](#encode_empty_table_as_object) | 12 | * [encode_empty_table_as_object](#encode_empty_table_as_object) |
| 13 | * [empty_array](#empty_array) | 13 | * [empty_array](#empty_array) |
| 14 | * [array_mt](#array_mt) | ||
| 14 | * [empty_array_mt](#empty_array_mt) | 15 | * [empty_array_mt](#empty_array_mt) |
| 15 | * [encode_number_precision](#encode_number_precision) | 16 | * [encode_number_precision](#encode_number_precision) |
| 16 | 17 | ||
| @@ -76,6 +77,39 @@ This will generate: | |||
| 76 | 77 | ||
| 77 | [Back to TOC](#table-of-contents) | 78 | [Back to TOC](#table-of-contents) |
| 78 | 79 | ||
| 80 | array_mt | ||
| 81 | -------- | ||
| 82 | **syntax:** `setmetatable({}, cjson.array_mt)` | ||
| 83 | |||
| 84 | When lua-cjson encodes a table with this metatable, it will systematically | ||
| 85 | encode it as a JSON Array. The resulting, encoded Array will contain the array | ||
| 86 | part of the table, and will be of the same length as the `#` operator on that | ||
| 87 | table. Holes in the table will be encoded with the `null` JSON value. | ||
| 88 | |||
| 89 | Example: | ||
| 90 | |||
| 91 | ```lua | ||
| 92 | local t = { "hello", "world" } | ||
| 93 | setmetatable(t, cjson.array_mt) | ||
| 94 | cjson.encode(t) -- ["hello","world"] | ||
| 95 | ``` | ||
| 96 | |||
| 97 | Or: | ||
| 98 | |||
| 99 | ```lua | ||
| 100 | local t = {} | ||
| 101 | t[1] = "one" | ||
| 102 | t[2] = "two" | ||
| 103 | t[4] = "three" | ||
| 104 | t.foo = "bar" | ||
| 105 | setmetatable(t, cjson.array_mt) | ||
| 106 | cjson.encode(t) -- ["one","two",null,"three"] | ||
| 107 | ``` | ||
| 108 | |||
| 109 | This value was introduced in the `2.1.0.5` release of this module. | ||
| 110 | |||
| 111 | [Back to TOC](#table-of-contents) | ||
| 112 | |||
| 79 | empty_array_mt | 113 | empty_array_mt |
| 80 | -------------- | 114 | -------------- |
| 81 | **syntax:** `setmetatable({}, cjson.empty_array_mt)` | 115 | **syntax:** `setmetatable({}, cjson.empty_array_mt)` |
diff --git a/lua_cjson.c b/lua_cjson.c index d04f151..8d6e313 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -92,6 +92,7 @@ | |||
| 92 | #endif | 92 | #endif |
| 93 | 93 | ||
| 94 | static const char * const *json_empty_array; | 94 | static const char * const *json_empty_array; |
| 95 | static const char * const *json_array; | ||
| 95 | 96 | ||
| 96 | typedef enum { | 97 | typedef enum { |
| 97 | T_OBJ_BEGIN, | 98 | T_OBJ_BEGIN, |
| @@ -713,21 +714,37 @@ static void json_append_data(lua_State *l, json_config_t *cfg, | |||
| 713 | case LUA_TTABLE: | 714 | case LUA_TTABLE: |
| 714 | current_depth++; | 715 | current_depth++; |
| 715 | json_check_encode_depth(l, cfg, current_depth, json); | 716 | json_check_encode_depth(l, cfg, current_depth, json); |
| 716 | len = lua_array_length(l, cfg, json); | 717 | |
| 717 | if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) | 718 | int as_array = 0; |
| 719 | int has_metatable = lua_getmetatable(l, -1); | ||
| 720 | |||
| 721 | if (has_metatable) { | ||
| 722 | lua_pushlightuserdata(l, &json_array); | ||
| 723 | lua_rawget(l, LUA_REGISTRYINDEX); | ||
| 724 | as_array = lua_rawequal(l, -1, -2); | ||
| 725 | lua_pop(l, 2); | ||
| 726 | } | ||
| 727 | |||
| 728 | if (as_array) { | ||
| 729 | len = lua_objlen(l, -1); | ||
| 718 | json_append_array(l, cfg, current_depth, json, len); | 730 | json_append_array(l, cfg, current_depth, json, len); |
| 719 | else { | 731 | } else { |
| 720 | int as_array = 0; | 732 | len = lua_array_length(l, cfg, json); |
| 721 | if (lua_getmetatable(l, -1)) { | ||
| 722 | lua_pushlightuserdata(l, &json_empty_array); | ||
| 723 | lua_rawget(l, LUA_REGISTRYINDEX); | ||
| 724 | as_array = lua_rawequal(l, -1, -2); | ||
| 725 | lua_pop(l, 2); | ||
| 726 | } | ||
| 727 | 733 | ||
| 728 | if (as_array) { | 734 | if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) { |
| 729 | json_append_array(l, cfg, current_depth, json, 0); | 735 | json_append_array(l, cfg, current_depth, json, len); |
| 730 | } else { | 736 | } else { |
| 737 | if (has_metatable) { | ||
| 738 | lua_getmetatable(l, -1); | ||
| 739 | lua_pushlightuserdata(l, &json_empty_array); | ||
| 740 | lua_rawget(l, LUA_REGISTRYINDEX); | ||
| 741 | as_array = lua_rawequal(l, -1, -2); | ||
| 742 | lua_pop(l, 2); /* pop pointer + metatable */ | ||
| 743 | if (as_array) { | ||
| 744 | json_append_array(l, cfg, current_depth, json, 0); | ||
| 745 | break; | ||
| 746 | } | ||
| 747 | } | ||
| 731 | json_append_object(l, cfg, current_depth, json); | 748 | json_append_object(l, cfg, current_depth, json); |
| 732 | } | 749 | } |
| 733 | } | 750 | } |
| @@ -738,7 +755,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, | |||
| 738 | case LUA_TLIGHTUSERDATA: | 755 | case LUA_TLIGHTUSERDATA: |
| 739 | if (lua_touserdata(l, -1) == NULL) { | 756 | if (lua_touserdata(l, -1) == NULL) { |
| 740 | strbuf_append_mem(json, "null", 4); | 757 | strbuf_append_mem(json, "null", 4); |
| 741 | } else if (lua_touserdata(l, -1) == &json_empty_array) { | 758 | } else if (lua_touserdata(l, -1) == &json_array) { |
| 742 | json_append_array(l, cfg, current_depth, json, 0); | 759 | json_append_array(l, cfg, current_depth, json, 0); |
| 743 | } | 760 | } |
| 744 | break; | 761 | break; |
| @@ -1412,20 +1429,27 @@ static int lua_cjson_new(lua_State *l) | |||
| 1412 | /* Initialise number conversions */ | 1429 | /* Initialise number conversions */ |
| 1413 | fpconv_init(); | 1430 | fpconv_init(); |
| 1414 | 1431 | ||
| 1415 | /* Test if empty array metatable is in registry */ | 1432 | /* Test if array metatables are in registry */ |
| 1416 | lua_pushlightuserdata(l, &json_empty_array); | 1433 | lua_pushlightuserdata(l, &json_empty_array); |
| 1417 | lua_rawget(l, LUA_REGISTRYINDEX); | 1434 | lua_rawget(l, LUA_REGISTRYINDEX); |
| 1418 | if (lua_isnil(l, -1)) { | 1435 | if (lua_isnil(l, -1)) { |
| 1419 | /* Create empty array metatable. | 1436 | /* Create array metatables. |
| 1420 | * | 1437 | * |
| 1421 | * If multiple calls to lua_cjson_new() are made, | 1438 | * If multiple calls to lua_cjson_new() are made, |
| 1422 | * this prevents overriding the table at the given | 1439 | * this prevents overriding the tables at the given |
| 1423 | * registry's index with a new one. | 1440 | * registry's index with a new one. |
| 1424 | */ | 1441 | */ |
| 1425 | lua_pop(l, 1); | 1442 | lua_pop(l, 1); |
| 1443 | |||
| 1444 | /* empty_array_mt */ | ||
| 1426 | lua_pushlightuserdata(l, &json_empty_array); | 1445 | lua_pushlightuserdata(l, &json_empty_array); |
| 1427 | lua_newtable(l); | 1446 | lua_newtable(l); |
| 1428 | lua_rawset(l, LUA_REGISTRYINDEX); | 1447 | lua_rawset(l, LUA_REGISTRYINDEX); |
| 1448 | |||
| 1449 | /* array_mt */ | ||
| 1450 | lua_pushlightuserdata(l, &json_array); | ||
| 1451 | lua_newtable(l); | ||
| 1452 | lua_rawset(l, LUA_REGISTRYINDEX); | ||
| 1429 | } | 1453 | } |
| 1430 | 1454 | ||
| 1431 | /* cjson module table */ | 1455 | /* cjson module table */ |
| @@ -1444,8 +1468,13 @@ static int lua_cjson_new(lua_State *l) | |||
| 1444 | lua_rawget(l, LUA_REGISTRYINDEX); | 1468 | lua_rawget(l, LUA_REGISTRYINDEX); |
| 1445 | lua_setfield(l, -2, "empty_array_mt"); | 1469 | lua_setfield(l, -2, "empty_array_mt"); |
| 1446 | 1470 | ||
| 1471 | /* Set cjson.array_mt */ | ||
| 1472 | lua_pushlightuserdata(l, &json_array); | ||
| 1473 | lua_rawget(l, LUA_REGISTRYINDEX); | ||
| 1474 | lua_setfield(l, -2, "array_mt"); | ||
| 1475 | |||
| 1447 | /* Set cjson.empty_array */ | 1476 | /* Set cjson.empty_array */ |
| 1448 | lua_pushlightuserdata(l, &json_empty_array); | 1477 | lua_pushlightuserdata(l, &json_array); |
| 1449 | lua_setfield(l, -2, "empty_array"); | 1478 | lua_setfield(l, -2, "empty_array"); |
| 1450 | 1479 | ||
| 1451 | /* Set module name / version fields */ | 1480 | /* Set module name / version fields */ |
diff --git a/tests/agentzh.t b/tests/agentzh.t index 0b281a1..dd70fb8 100644 --- a/tests/agentzh.t +++ b/tests/agentzh.t | |||
| @@ -116,7 +116,78 @@ print(cjson.encode(data)) | |||
| 116 | 116 | ||
| 117 | 117 | ||
| 118 | 118 | ||
| 119 | === TEST 9: multiple calls to lua_cjson_new (1/2) | 119 | === TEST 9: array_mt on empty tables |
| 120 | --- lua | ||
| 121 | local cjson = require "cjson" | ||
| 122 | local data = {} | ||
| 123 | setmetatable(data, cjson.array_mt) | ||
| 124 | print(cjson.encode(data)) | ||
| 125 | --- out | ||
| 126 | [] | ||
| 127 | |||
| 128 | |||
| 129 | |||
| 130 | === TEST 10: array_mt on non-empty tables | ||
| 131 | --- lua | ||
| 132 | local cjson = require "cjson" | ||
| 133 | local data = { "foo", "bar" } | ||
| 134 | setmetatable(data, cjson.array_mt) | ||
| 135 | print(cjson.encode(data)) | ||
| 136 | --- out | ||
| 137 | ["foo","bar"] | ||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | === TEST 11: array_mt on non-empty tables with holes | ||
| 142 | --- lua | ||
| 143 | local cjson = require "cjson" | ||
| 144 | local data = {} | ||
| 145 | data[1] = "foo" | ||
| 146 | data[2] = "bar" | ||
| 147 | data[4] = "last" | ||
| 148 | data[9] = "none" | ||
| 149 | setmetatable(data, cjson.array_mt) | ||
| 150 | print(cjson.encode(data)) | ||
| 151 | --- out | ||
| 152 | ["foo","bar",null,"last"] | ||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | === TEST 12: array_mt on tables with hash part | ||
| 157 | --- lua | ||
| 158 | local cjson = require "cjson" | ||
| 159 | local data | ||
| 160 | |||
| 161 | if jit and string.find(jit.version, "LuaJIT 2.1.0", nil, true) then | ||
| 162 | local new_tab = require "table.new" | ||
| 163 | data = new_tab(0, 2) -- allocating hash part only | ||
| 164 | |||
| 165 | else | ||
| 166 | data = {} | ||
| 167 | end | ||
| 168 | |||
| 169 | data.foo = "bar" | ||
| 170 | data[1] = "hello" | ||
| 171 | setmetatable(data, cjson.array_mt) | ||
| 172 | print(cjson.encode(data)) | ||
| 173 | --- out | ||
| 174 | ["hello"] | ||
| 175 | |||
| 176 | |||
| 177 | |||
| 178 | === TEST 13: multiple calls to lua_cjson_new (1/3) | ||
| 179 | --- lua | ||
| 180 | local cjson = require "cjson" | ||
| 181 | package.loaded["cjson"] = nil | ||
| 182 | require "cjson" | ||
| 183 | local arr = setmetatable({}, cjson.array_mt) | ||
| 184 | print(cjson.encode(arr)) | ||
| 185 | --- out | ||
| 186 | [] | ||
| 187 | |||
| 188 | |||
| 189 | |||
| 190 | === TEST 14: multiple calls to lua_cjson_new (2/3) | ||
| 120 | --- lua | 191 | --- lua |
| 121 | local cjson = require "cjson" | 192 | local cjson = require "cjson" |
| 122 | package.loaded["cjson"] = nil | 193 | package.loaded["cjson"] = nil |
| @@ -128,7 +199,7 @@ print(cjson.encode(arr)) | |||
| 128 | 199 | ||
| 129 | 200 | ||
| 130 | 201 | ||
| 131 | === TEST 10: multiple calls to lua_cjson_new (2/2) | 202 | === TEST 15: multiple calls to lua_cjson_new (3/3) |
| 132 | --- lua | 203 | --- lua |
| 133 | local cjson = require "cjson.safe" | 204 | local cjson = require "cjson.safe" |
| 134 | -- load another cjson instance (not in package.loaded) | 205 | -- load another cjson instance (not in package.loaded) |
| @@ -140,7 +211,7 @@ print(cjson.encode(arr)) | |||
| 140 | 211 | ||
| 141 | 212 | ||
| 142 | 213 | ||
| 143 | === TEST 11: & in JSON | 214 | === TEST 16: & in JSON |
| 144 | --- lua | 215 | --- lua |
| 145 | local cjson = require "cjson" | 216 | local cjson = require "cjson" |
| 146 | local a="[\"a=1&b=2\"]" | 217 | local a="[\"a=1&b=2\"]" |
| @@ -151,7 +222,7 @@ print(cjson.encode(b)) | |||
| 151 | 222 | ||
| 152 | 223 | ||
| 153 | 224 | ||
| 154 | === TEST 12: default and max precision | 225 | === TEST 17: default and max precision |
| 155 | --- lua | 226 | --- lua |
| 156 | local math = require "math" | 227 | local math = require "math" |
| 157 | local cjson = require "cjson" | 228 | local cjson = require "cjson" |
