diff options
| author | Thibault Charbonnier <thibaultcha@me.com> | 2017-08-05 15:19:43 -0700 |
|---|---|---|
| committer | Yichun Zhang (agentzh) <agentzh@gmail.com> | 2017-11-17 11:46:46 -0800 |
| commit | b5e364c7c60167995944ed3a3b9c54d9a377fc1d (patch) | |
| tree | b2554317191a34b155aca68be0a54ef364296b58 | |
| parent | 5f9efa4829a72935ddcd40c7da6b1a9e10939b65 (diff) | |
| download | lua-cjson-2.1.0.6rc1.tar.gz lua-cjson-2.1.0.6rc1.tar.bz2 lua-cjson-2.1.0.6rc1.zip | |
feature: set cjson.array_mt on decoded JSON arrays.2.1.0.6rc1
this can be turned on via cjson.decode_array_with_array_mt(true). off by
default.
Signed-off-by: Yichun Zhang (agentzh) <agentzh@gmail.com>
| -rw-r--r-- | README.md | 37 | ||||
| -rw-r--r-- | lua_cjson.c | 21 | ||||
| -rw-r--r-- | tests/agentzh.t | 61 |
3 files changed, 113 insertions, 6 deletions
| @@ -14,6 +14,7 @@ Table of Contents | |||
| 14 | * [array_mt](#array_mt) | 14 | * [array_mt](#array_mt) |
| 15 | * [empty_array_mt](#empty_array_mt) | 15 | * [empty_array_mt](#empty_array_mt) |
| 16 | * [encode_number_precision](#encode_number_precision) | 16 | * [encode_number_precision](#encode_number_precision) |
| 17 | * [decode_array_with_array_mt](#decode_array_with_array_mt) | ||
| 17 | 18 | ||
| 18 | Description | 19 | Description |
| 19 | =========== | 20 | =========== |
| @@ -156,3 +157,39 @@ encode_number_precision | |||
| 156 | This fork allows encoding of numbers with a `precision` up to 16 decimals (vs. 14 in mpx/lua-cjson). | 157 | This fork allows encoding of numbers with a `precision` up to 16 decimals (vs. 14 in mpx/lua-cjson). |
| 157 | 158 | ||
| 158 | [Back to TOC](#table-of-contents) | 159 | [Back to TOC](#table-of-contents) |
| 160 | |||
| 161 | decode_array_with_array_mt | ||
| 162 | -------------------------- | ||
| 163 | **syntax:** `cjson.decode_array_with_array_mt(enabled)` | ||
| 164 | |||
| 165 | **default:** false | ||
| 166 | |||
| 167 | If enabled, JSON Arrays decoded by `cjson.decode` will result in Lua | ||
| 168 | tables with the [`array_mt`](#array_mt) metatable. This can ensure a 1-to-1 | ||
| 169 | relationship between arrays upon multiple encoding/decoding of your | ||
| 170 | JSON data with this module. | ||
| 171 | |||
| 172 | If disabled, JSON Arrays will be decoded to plain Lua tables, without | ||
| 173 | the `array_mt` metatable. | ||
| 174 | |||
| 175 | The `enabled` argument is a boolean. | ||
| 176 | |||
| 177 | Example: | ||
| 178 | |||
| 179 | ```lua | ||
| 180 | local cjson = require "cjson" | ||
| 181 | |||
| 182 | -- default behavior | ||
| 183 | local my_json = [[{"my_array":[]}]] | ||
| 184 | local t = cjson.decode(my_json) | ||
| 185 | cjson.encode(t) -- {"my_array":{}} back to an object | ||
| 186 | |||
| 187 | -- now, if this behavior is enabled | ||
| 188 | cjson.decode_array_with_array_mt(true) | ||
| 189 | |||
| 190 | local my_json = [[{"my_array":[]}]] | ||
| 191 | local t = cjson.decode(my_json) | ||
| 192 | cjson.encode(t) -- {"my_array":[]} properly re-encoded as an array | ||
| 193 | ``` | ||
| 194 | |||
| 195 | [Back to TOC](#table-of-contents) | ||
diff --git a/lua_cjson.c b/lua_cjson.c index 8d6e313..6c7eb4e 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -79,6 +79,7 @@ | |||
| 79 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 | 79 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 |
| 80 | #define DEFAULT_ENCODE_NUMBER_PRECISION 14 | 80 | #define DEFAULT_ENCODE_NUMBER_PRECISION 14 |
| 81 | #define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1 | 81 | #define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1 |
| 82 | #define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0 | ||
| 82 | 83 | ||
| 83 | #ifdef DISABLE_INVALID_NUMBERS | 84 | #ifdef DISABLE_INVALID_NUMBERS |
| 84 | #undef DEFAULT_DECODE_INVALID_NUMBERS | 85 | #undef DEFAULT_DECODE_INVALID_NUMBERS |
| @@ -148,6 +149,7 @@ typedef struct { | |||
| 148 | 149 | ||
| 149 | int decode_invalid_numbers; | 150 | int decode_invalid_numbers; |
| 150 | int decode_max_depth; | 151 | int decode_max_depth; |
| 152 | int decode_array_with_array_mt; | ||
| 151 | } json_config_t; | 153 | } json_config_t; |
| 152 | 154 | ||
| 153 | typedef struct { | 155 | typedef struct { |
| @@ -329,6 +331,16 @@ static int json_cfg_encode_empty_table_as_object(lua_State *l) | |||
| 329 | return json_enum_option(l, 1, &cfg->encode_empty_table_as_object, NULL, 1); | 331 | return json_enum_option(l, 1, &cfg->encode_empty_table_as_object, NULL, 1); |
| 330 | } | 332 | } |
| 331 | 333 | ||
| 334 | /* Configures how to decode arrays */ | ||
| 335 | static int json_cfg_decode_array_with_array_mt(lua_State *l) | ||
| 336 | { | ||
| 337 | json_config_t *cfg = json_arg_init(l, 1); | ||
| 338 | |||
| 339 | json_enum_option(l, 1, &cfg->decode_array_with_array_mt, NULL, 1); | ||
| 340 | |||
| 341 | return 1; | ||
| 342 | } | ||
| 343 | |||
| 332 | /* Configures JSON encoding buffer persistence */ | 344 | /* Configures JSON encoding buffer persistence */ |
| 333 | static int json_cfg_encode_keep_buffer(lua_State *l) | 345 | static int json_cfg_encode_keep_buffer(lua_State *l) |
| 334 | { | 346 | { |
| @@ -420,6 +432,7 @@ static void json_create_config(lua_State *l) | |||
| 420 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; | 432 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; |
| 421 | cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; | 433 | cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; |
| 422 | cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT; | 434 | cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT; |
| 435 | cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT; | ||
| 423 | 436 | ||
| 424 | #if DEFAULT_ENCODE_KEEP_BUFFER > 0 | 437 | #if DEFAULT_ENCODE_KEEP_BUFFER > 0 |
| 425 | strbuf_init(&cfg->encode_buf, 0); | 438 | strbuf_init(&cfg->encode_buf, 0); |
| @@ -1261,6 +1274,13 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) | |||
| 1261 | 1274 | ||
| 1262 | lua_newtable(l); | 1275 | lua_newtable(l); |
| 1263 | 1276 | ||
| 1277 | /* set array_mt on the table at the top of the stack */ | ||
| 1278 | if (json->cfg->decode_array_with_array_mt) { | ||
| 1279 | lua_pushlightuserdata(l, &json_array); | ||
| 1280 | lua_rawget(l, LUA_REGISTRYINDEX); | ||
| 1281 | lua_setmetatable(l, -2); | ||
| 1282 | } | ||
| 1283 | |||
| 1264 | json_next_token(json, &token); | 1284 | json_next_token(json, &token); |
| 1265 | 1285 | ||
| 1266 | /* Handle empty arrays */ | 1286 | /* Handle empty arrays */ |
| @@ -1415,6 +1435,7 @@ static int lua_cjson_new(lua_State *l) | |||
| 1415 | { "encode", json_encode }, | 1435 | { "encode", json_encode }, |
| 1416 | { "decode", json_decode }, | 1436 | { "decode", json_decode }, |
| 1417 | { "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object }, | 1437 | { "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object }, |
| 1438 | { "decode_array_with_array_mt", json_cfg_decode_array_with_array_mt }, | ||
| 1418 | { "encode_sparse_array", json_cfg_encode_sparse_array }, | 1439 | { "encode_sparse_array", json_cfg_encode_sparse_array }, |
| 1419 | { "encode_max_depth", json_cfg_encode_max_depth }, | 1440 | { "encode_max_depth", json_cfg_encode_max_depth }, |
| 1420 | { "decode_max_depth", json_cfg_decode_max_depth }, | 1441 | { "decode_max_depth", json_cfg_decode_max_depth }, |
diff --git a/tests/agentzh.t b/tests/agentzh.t index dd70fb8..7967337 100644 --- a/tests/agentzh.t +++ b/tests/agentzh.t | |||
| @@ -153,7 +153,56 @@ print(cjson.encode(data)) | |||
| 153 | 153 | ||
| 154 | 154 | ||
| 155 | 155 | ||
| 156 | === TEST 12: array_mt on tables with hash part | 156 | === TEST 12: decode() by default does not set array_mt on empty arrays |
| 157 | --- lua | ||
| 158 | local cjson = require "cjson" | ||
| 159 | local json = [[{"my_array":[]}]] | ||
| 160 | local t = cjson.decode(json) | ||
| 161 | local has_metatable = getmetatable(t.my_array) == cjson.array_mt | ||
| 162 | print("decoded JSON array has metatable: " .. tostring(has_metatable)) | ||
| 163 | print(cjson.encode(t)) | ||
| 164 | --- out | ||
| 165 | decoded JSON array has metatable: false | ||
| 166 | {"my_array":{}} | ||
| 167 | |||
| 168 | |||
| 169 | |||
| 170 | === TEST 13: decode() sets array_mt on non-empty arrays if enabled | ||
| 171 | --- lua | ||
| 172 | local cjson = require "cjson" | ||
| 173 | cjson.decode_array_with_array_mt(true) | ||
| 174 | local json = [[{"my_array":["hello","world"]}]] | ||
| 175 | local t = cjson.decode(json) | ||
| 176 | t.my_array.hash_value = "adding a hash value" | ||
| 177 | -- emptying the array part | ||
| 178 | t.my_array[1] = nil | ||
| 179 | t.my_array[2] = nil | ||
| 180 | local has_metatable = getmetatable(t.my_array) == cjson.array_mt | ||
| 181 | print("decoded JSON array has metatable: " .. tostring(has_metatable)) | ||
| 182 | print(cjson.encode(t)) | ||
| 183 | --- out | ||
| 184 | decoded JSON array has metatable: true | ||
| 185 | {"my_array":[]} | ||
| 186 | |||
| 187 | |||
| 188 | |||
| 189 | === TEST 14: cfg can enable/disable setting array_mt | ||
| 190 | --- lua | ||
| 191 | local cjson = require "cjson" | ||
| 192 | cjson.decode_array_with_array_mt(true) | ||
| 193 | cjson.decode_array_with_array_mt(false) | ||
| 194 | local json = [[{"my_array":[]}]] | ||
| 195 | local t = cjson.decode(json) | ||
| 196 | local has_metatable = getmetatable(t.my_array) == cjson.array_mt | ||
| 197 | print("decoded JSON array has metatable: " .. tostring(has_metatable)) | ||
| 198 | print(cjson.encode(t)) | ||
| 199 | --- out | ||
| 200 | decoded JSON array has metatable: false | ||
| 201 | {"my_array":{}} | ||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | === TEST 15: array_mt on tables with hash part | ||
| 157 | --- lua | 206 | --- lua |
| 158 | local cjson = require "cjson" | 207 | local cjson = require "cjson" |
| 159 | local data | 208 | local data |
| @@ -175,7 +224,7 @@ print(cjson.encode(data)) | |||
| 175 | 224 | ||
| 176 | 225 | ||
| 177 | 226 | ||
| 178 | === TEST 13: multiple calls to lua_cjson_new (1/3) | 227 | === TEST 16: multiple calls to lua_cjson_new (1/3) |
| 179 | --- lua | 228 | --- lua |
| 180 | local cjson = require "cjson" | 229 | local cjson = require "cjson" |
| 181 | package.loaded["cjson"] = nil | 230 | package.loaded["cjson"] = nil |
| @@ -187,7 +236,7 @@ print(cjson.encode(arr)) | |||
| 187 | 236 | ||
| 188 | 237 | ||
| 189 | 238 | ||
| 190 | === TEST 14: multiple calls to lua_cjson_new (2/3) | 239 | === TEST 17: multiple calls to lua_cjson_new (2/3) |
| 191 | --- lua | 240 | --- lua |
| 192 | local cjson = require "cjson" | 241 | local cjson = require "cjson" |
| 193 | package.loaded["cjson"] = nil | 242 | package.loaded["cjson"] = nil |
| @@ -199,7 +248,7 @@ print(cjson.encode(arr)) | |||
| 199 | 248 | ||
| 200 | 249 | ||
| 201 | 250 | ||
| 202 | === TEST 15: multiple calls to lua_cjson_new (3/3) | 251 | === TEST 18: multiple calls to lua_cjson_new (3/3) |
| 203 | --- lua | 252 | --- lua |
| 204 | local cjson = require "cjson.safe" | 253 | local cjson = require "cjson.safe" |
| 205 | -- load another cjson instance (not in package.loaded) | 254 | -- load another cjson instance (not in package.loaded) |
| @@ -211,7 +260,7 @@ print(cjson.encode(arr)) | |||
| 211 | 260 | ||
| 212 | 261 | ||
| 213 | 262 | ||
| 214 | === TEST 16: & in JSON | 263 | === TEST 19: & in JSON |
| 215 | --- lua | 264 | --- lua |
| 216 | local cjson = require "cjson" | 265 | local cjson = require "cjson" |
| 217 | local a="[\"a=1&b=2\"]" | 266 | local a="[\"a=1&b=2\"]" |
| @@ -222,7 +271,7 @@ print(cjson.encode(b)) | |||
| 222 | 271 | ||
| 223 | 272 | ||
| 224 | 273 | ||
| 225 | === TEST 17: default and max precision | 274 | === TEST 20: default and max precision |
| 226 | --- lua | 275 | --- lua |
| 227 | local math = require "math" | 276 | local math = require "math" |
| 228 | local cjson = require "cjson" | 277 | local cjson = require "cjson" |
