aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md34
-rw-r--r--lua_cjson.c63
-rw-r--r--tests/agentzh.t79
3 files changed, 155 insertions, 21 deletions
diff --git a/README.md b/README.md
index 7282a4d..1cdb53b 100644
--- a/README.md
+++ b/README.md
@@ -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
80array_mt
81--------
82**syntax:** `setmetatable({}, cjson.array_mt)`
83
84When lua-cjson encodes a table with this metatable, it will systematically
85encode it as a JSON Array. The resulting, encoded Array will contain the array
86part of the table, and will be of the same length as the `#` operator on that
87table. Holes in the table will be encoded with the `null` JSON value.
88
89Example:
90
91```lua
92local t = { "hello", "world" }
93setmetatable(t, cjson.array_mt)
94cjson.encode(t) -- ["hello","world"]
95```
96
97Or:
98
99```lua
100local t = {}
101t[1] = "one"
102t[2] = "two"
103t[4] = "three"
104t.foo = "bar"
105setmetatable(t, cjson.array_mt)
106cjson.encode(t) -- ["one","two",null,"three"]
107```
108
109This value was introduced in the `2.1.0.5` release of this module.
110
111[Back to TOC](#table-of-contents)
112
79empty_array_mt 113empty_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
94static const char * const *json_empty_array; 94static const char * const *json_empty_array;
95static const char * const *json_array;
95 96
96typedef enum { 97typedef 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
121local cjson = require "cjson"
122local data = {}
123setmetatable(data, cjson.array_mt)
124print(cjson.encode(data))
125--- out
126[]
127
128
129
130=== TEST 10: array_mt on non-empty tables
131--- lua
132local cjson = require "cjson"
133local data = { "foo", "bar" }
134setmetatable(data, cjson.array_mt)
135print(cjson.encode(data))
136--- out
137["foo","bar"]
138
139
140
141=== TEST 11: array_mt on non-empty tables with holes
142--- lua
143local cjson = require "cjson"
144local data = {}
145data[1] = "foo"
146data[2] = "bar"
147data[4] = "last"
148data[9] = "none"
149setmetatable(data, cjson.array_mt)
150print(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
158local cjson = require "cjson"
159local data
160
161if 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
165else
166 data = {}
167end
168
169data.foo = "bar"
170data[1] = "hello"
171setmetatable(data, cjson.array_mt)
172print(cjson.encode(data))
173--- out
174["hello"]
175
176
177
178=== TEST 13: multiple calls to lua_cjson_new (1/3)
179--- lua
180local cjson = require "cjson"
181package.loaded["cjson"] = nil
182require "cjson"
183local arr = setmetatable({}, cjson.array_mt)
184print(cjson.encode(arr))
185--- out
186[]
187
188
189
190=== TEST 14: multiple calls to lua_cjson_new (2/3)
120--- lua 191--- lua
121local cjson = require "cjson" 192local cjson = require "cjson"
122package.loaded["cjson"] = nil 193package.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
133local cjson = require "cjson.safe" 204local 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
145local cjson = require "cjson" 216local cjson = require "cjson"
146local a="[\"a=1&b=2\"]" 217local 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
156local math = require "math" 227local math = require "math"
157local cjson = require "cjson" 228local cjson = require "cjson"