diff options
| author | skewb1k <skewb1kunix@gmail.com> | 2026-02-10 03:59:11 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-10 08:59:11 +0800 |
| commit | fed8c8356be4d94ceb55f2880bdb77fd9b55ef1e (patch) | |
| tree | 07a6044110a57d509da45f99ae0a80054386abc7 | |
| parent | ea05d06f5a56d94d423fc697561a33ca3b8bf085 (diff) | |
| download | lua-cjson-fed8c8356be4d94ceb55f2880bdb77fd9b55ef1e.tar.gz lua-cjson-fed8c8356be4d94ceb55f2880bdb77fd9b55ef1e.tar.bz2 lua-cjson-fed8c8356be4d94ceb55f2880bdb77fd9b55ef1e.zip | |
feature: add option to allow comments in decode.
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | lua_cjson.c | 57 | ||||
| -rwxr-xr-x | tests/test.lua | 26 |
3 files changed, 91 insertions, 5 deletions
| @@ -262,3 +262,16 @@ cjson.encode(t) -- {"my_array":[]} properly re-encoded as an array | |||
| 262 | ``` | 262 | ``` |
| 263 | 263 | ||
| 264 | [Back to TOC](#table-of-contents) | 264 | [Back to TOC](#table-of-contents) |
| 265 | |||
| 266 | decode_allow_comments | ||
| 267 | -------------------------- | ||
| 268 | **syntax:** `cjson.decode_allow_comments(enabled)` | ||
| 269 | |||
| 270 | **default:** false | ||
| 271 | |||
| 272 | If enabled, allows JavaScript-style comments in `cjson.decode` input. Comments | ||
| 273 | are treated as whitespace and may appear anywhere whitespace is valid in JSON. | ||
| 274 | Supports single-line comments beginning with '//' and block comments enclosed | ||
| 275 | with '/* ... */'. | ||
| 276 | |||
| 277 | [Back to TOC](#table-of-contents) | ||
diff --git a/lua_cjson.c b/lua_cjson.c index 789817d..de77f26 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -89,6 +89,7 @@ | |||
| 89 | #define DEFAULT_ENCODE_NUMBER_PRECISION 14 | 89 | #define DEFAULT_ENCODE_NUMBER_PRECISION 14 |
| 90 | #define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1 | 90 | #define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1 |
| 91 | #define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0 | 91 | #define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0 |
| 92 | #define DEFAULT_DECODE_ALLOW_COMMENTS 0 | ||
| 92 | #define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1 | 93 | #define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1 |
| 93 | #define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0 | 94 | #define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0 |
| 94 | #define DEFAULT_ENCODE_INDENT NULL | 95 | #define DEFAULT_ENCODE_INDENT NULL |
| @@ -178,6 +179,7 @@ typedef struct { | |||
| 178 | int decode_invalid_numbers; | 179 | int decode_invalid_numbers; |
| 179 | int decode_max_depth; | 180 | int decode_max_depth; |
| 180 | int decode_array_with_array_mt; | 181 | int decode_array_with_array_mt; |
| 182 | int decode_allow_comments; | ||
| 181 | int encode_skip_unsupported_value_types; | 183 | int encode_skip_unsupported_value_types; |
| 182 | } json_config_t; | 184 | } json_config_t; |
| 183 | 185 | ||
| @@ -383,6 +385,16 @@ static int json_cfg_decode_array_with_array_mt(lua_State *l) | |||
| 383 | return 1; | 385 | return 1; |
| 384 | } | 386 | } |
| 385 | 387 | ||
| 388 | /* Configures whether decoder allows comments */ | ||
| 389 | static int json_cfg_decode_allow_comments(lua_State *l) | ||
| 390 | { | ||
| 391 | json_config_t *cfg = json_arg_init(l, 1); | ||
| 392 | |||
| 393 | json_enum_option(l, 1, &cfg->decode_allow_comments, NULL, 1); | ||
| 394 | |||
| 395 | return 1; | ||
| 396 | } | ||
| 397 | |||
| 386 | /* Configure how to treat invalid types */ | 398 | /* Configure how to treat invalid types */ |
| 387 | static int json_cfg_encode_skip_unsupported_value_types(lua_State *l) | 399 | static int json_cfg_encode_skip_unsupported_value_types(lua_State *l) |
| 388 | { | 400 | { |
| @@ -515,6 +527,7 @@ static void json_create_config(lua_State *l) | |||
| 515 | cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; | 527 | cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; |
| 516 | cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT; | 528 | cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT; |
| 517 | cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT; | 529 | cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT; |
| 530 | cfg->decode_allow_comments = DEFAULT_DECODE_ALLOW_COMMENTS; | ||
| 518 | cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH; | 531 | cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH; |
| 519 | cfg->encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES; | 532 | cfg->encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES; |
| 520 | cfg->encode_indent = DEFAULT_ENCODE_INDENT; | 533 | cfg->encode_indent = DEFAULT_ENCODE_INDENT; |
| @@ -1279,13 +1292,46 @@ static void json_next_token(json_parse_t *json, json_token_t *token) | |||
| 1279 | const json_token_type_t *ch2token = json->cfg->ch2token; | 1292 | const json_token_type_t *ch2token = json->cfg->ch2token; |
| 1280 | int ch; | 1293 | int ch; |
| 1281 | 1294 | ||
| 1282 | /* Eat whitespace. */ | ||
| 1283 | while (1) { | 1295 | while (1) { |
| 1284 | ch = (unsigned char)*(json->ptr); | 1296 | /* Eat whitespace. */ |
| 1285 | token->type = ch2token[ch]; | 1297 | while (1) { |
| 1286 | if (token->type != T_WHITESPACE) | 1298 | ch = (unsigned char)*(json->ptr); |
| 1299 | token->type = ch2token[ch]; | ||
| 1300 | if (token->type != T_WHITESPACE) | ||
| 1301 | break; | ||
| 1302 | json->ptr++; | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | if (!json->cfg->decode_allow_comments) | ||
| 1287 | break; | 1306 | break; |
| 1288 | json->ptr++; | 1307 | |
| 1308 | /* Eat comments. */ | ||
| 1309 | if ((unsigned char)json->ptr[0] != '/' || | ||
| 1310 | ((unsigned char)json->ptr[1] != '/' && | ||
| 1311 | (unsigned char)json->ptr[1] != '*')) { | ||
| 1312 | break; | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | if (json->ptr[1] == '/') { | ||
| 1316 | /* Handle single-line comment */ | ||
| 1317 | json->ptr += 2; | ||
| 1318 | while (*json->ptr != '\0' && *json->ptr != '\n') | ||
| 1319 | json->ptr++; | ||
| 1320 | } else { | ||
| 1321 | /* Handle multi-line comment */ | ||
| 1322 | json->ptr += 2; | ||
| 1323 | while (1) { | ||
| 1324 | if (*json->ptr == '\0') { | ||
| 1325 | json_set_token_error(token, json, "unclosed multi-line comment"); | ||
| 1326 | return; | ||
| 1327 | } | ||
| 1328 | if (json->ptr[0] == '*' && json->ptr[1] == '/') { | ||
| 1329 | json->ptr += 2; | ||
| 1330 | break; | ||
| 1331 | } | ||
| 1332 | json->ptr++; | ||
| 1333 | } | ||
| 1334 | } | ||
| 1289 | } | 1335 | } |
| 1290 | 1336 | ||
| 1291 | /* Store location of new token. Required when throwing errors | 1337 | /* Store location of new token. Required when throwing errors |
| @@ -1625,6 +1671,7 @@ static int lua_cjson_new(lua_State *l) | |||
| 1625 | { "decode", json_decode }, | 1671 | { "decode", json_decode }, |
| 1626 | { "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object }, | 1672 | { "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object }, |
| 1627 | { "decode_array_with_array_mt", json_cfg_decode_array_with_array_mt }, | 1673 | { "decode_array_with_array_mt", json_cfg_decode_array_with_array_mt }, |
| 1674 | { "decode_allow_comments", json_cfg_decode_allow_comments }, | ||
| 1628 | { "encode_sparse_array", json_cfg_encode_sparse_array }, | 1675 | { "encode_sparse_array", json_cfg_encode_sparse_array }, |
| 1629 | { "encode_max_depth", json_cfg_encode_max_depth }, | 1676 | { "encode_max_depth", json_cfg_encode_max_depth }, |
| 1630 | { "decode_max_depth", json_cfg_decode_max_depth }, | 1677 | { "decode_max_depth", json_cfg_decode_max_depth }, |
diff --git a/tests/test.lua b/tests/test.lua index 0513d97..068a9f0 100755 --- a/tests/test.lua +++ b/tests/test.lua | |||
| @@ -333,6 +333,32 @@ local cjson_tests = { | |||
| 333 | json.decode, { [["\uDB00\uD"]] }, | 333 | json.decode, { [["\uDB00\uD"]] }, |
| 334 | false, { "Expected value but found invalid unicode escape code at character 2" } }, | 334 | false, { "Expected value but found invalid unicode escape code at character 2" } }, |
| 335 | 335 | ||
| 336 | -- Test comments | ||
| 337 | { 'Set decode_allow_comments(true)', | ||
| 338 | json.decode_allow_comments, { true }, true, { true } }, | ||
| 339 | { "Decode single-line comment", | ||
| 340 | json.decode, { '{//comment\n}' }, true, { {} } }, | ||
| 341 | { "Decode single-line comment with windows newline", | ||
| 342 | json.decode, { '{//comment\r\n}' }, true, { {} } }, | ||
| 343 | { "Decode single-line comment after string value", | ||
| 344 | json.decode, { '"test // /* */ string"//comment' }, true, { "test // /* */ string" } }, | ||
| 345 | { "Decode multi-line comment", | ||
| 346 | json.decode, { '{/* A multi-line\ncomment*/}' }, true, { {} } }, | ||
| 347 | { "Decode multi-line comment before colon", | ||
| 348 | json.decode, { '{"a" /* Comment */: 1}' }, true, { { a = 1 } } }, | ||
| 349 | { "Decode multi-line comment after colon", | ||
| 350 | json.decode, { '{"a": /* Comment */ 1}' }, true, { { a = 1 } } }, | ||
| 351 | { "Decode multiple comments in a row", | ||
| 352 | json.decode, { '/*first*//*second*/{}' }, true, { {} } }, | ||
| 353 | { "Decode unclosed multi-line comment [throw error]", | ||
| 354 | json.decode, { '{}/*Unclosed' }, | ||
| 355 | false, { "Expected the end but found unclosed multi-line comment at character 13" } }, | ||
| 356 | { "Decode comment inside number [throw error]", | ||
| 357 | json.decode, { '{"a":1/*x*/0}' }, | ||
| 358 | false, { "Expected comma or object end but found T_INTEGER at character 12" } }, | ||
| 359 | { 'Set decode_allow_comments(false)', | ||
| 360 | json.decode_allow_comments, { false }, true, { false } }, | ||
| 361 | |||
| 336 | -- Test indenting | 362 | -- Test indenting |
| 337 | { 'Set encode_indent(" ")', | 363 | { 'Set encode_indent(" ")', |
| 338 | json.encode_indent, { " " }, true, { " " } }, | 364 | json.encode_indent, { " " }, true, { " " } }, |
