Skip to content

Commit fed8c83

Browse files
authored
feature: add option to allow comments in decode.
1 parent ea05d06 commit fed8c83

3 files changed

Lines changed: 91 additions & 5 deletions

File tree

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,16 @@ cjson.encode(t) -- {"my_array":[]} properly re-encoded as an array
262262
```
263263

264264
[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)

lua_cjson.c

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#define DEFAULT_ENCODE_NUMBER_PRECISION 14
9090
#define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1
9191
#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0
92+
#define DEFAULT_DECODE_ALLOW_COMMENTS 0
9293
#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1
9394
#define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0
9495
#define DEFAULT_ENCODE_INDENT NULL
@@ -178,6 +179,7 @@ typedef struct {
178179
int decode_invalid_numbers;
179180
int decode_max_depth;
180181
int decode_array_with_array_mt;
182+
int decode_allow_comments;
181183
int encode_skip_unsupported_value_types;
182184
} json_config_t;
183185

@@ -383,6 +385,16 @@ static int json_cfg_decode_array_with_array_mt(lua_State *l)
383385
return 1;
384386
}
385387

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+
386398
/* Configure how to treat invalid types */
387399
static int json_cfg_encode_skip_unsupported_value_types(lua_State *l)
388400
{
@@ -515,6 +527,7 @@ static void json_create_config(lua_State *l)
515527
cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION;
516528
cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT;
517529
cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT;
530+
cfg->decode_allow_comments = DEFAULT_DECODE_ALLOW_COMMENTS;
518531
cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH;
519532
cfg->encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES;
520533
cfg->encode_indent = DEFAULT_ENCODE_INDENT;
@@ -1279,13 +1292,46 @@ static void json_next_token(json_parse_t *json, json_token_t *token)
12791292
const json_token_type_t *ch2token = json->cfg->ch2token;
12801293
int ch;
12811294

1282-
/* Eat whitespace. */
12831295
while (1) {
1284-
ch = (unsigned char)*(json->ptr);
1285-
token->type = ch2token[ch];
1286-
if (token->type != T_WHITESPACE)
1296+
/* Eat whitespace. */
1297+
while (1) {
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)
12871306
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+
}
12891335
}
12901336

12911337
/* Store location of new token. Required when throwing errors
@@ -1625,6 +1671,7 @@ static int lua_cjson_new(lua_State *l)
16251671
{ "decode", json_decode },
16261672
{ "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object },
16271673
{ "decode_array_with_array_mt", json_cfg_decode_array_with_array_mt },
1674+
{ "decode_allow_comments", json_cfg_decode_allow_comments },
16281675
{ "encode_sparse_array", json_cfg_encode_sparse_array },
16291676
{ "encode_max_depth", json_cfg_encode_max_depth },
16301677
{ "decode_max_depth", json_cfg_decode_max_depth },

tests/test.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,32 @@ local cjson_tests = {
333333
json.decode, { [["\uDB00\uD"]] },
334334
false, { "Expected value but found invalid unicode escape code at character 2" } },
335335

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+
336362
-- Test indenting
337363
{ 'Set encode_indent(" ")',
338364
json.encode_indent, { " " }, true, { " " } },

0 commit comments

Comments
 (0)