Skip to content

Commit ef91aea

Browse files
byrootjspanjers
andcommitted
Add option :allow_trailing_comma to JSON#parse
Fix: ruby#401 Since we already accept comments without even a flag to turn it off, it makes sense to go just one small bit farther and optionally allow trailing comma so we have decent support for what is generally defined as "JSONC" and frequently used for configuration files. Co-Authored-By: Jan-Joost Spanjers <oss@hiberis.nl>
1 parent 4af700e commit ef91aea

File tree

8 files changed

+1122
-391
lines changed

8 files changed

+1122
-391
lines changed

ext/json/ext/parser/parser.c

Lines changed: 677 additions & 233 deletions
Large diffs are not rendered by default.

ext/json/ext/parser/parser.rl

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id,
88
i_chr, i_deep_const_get, i_match, i_aset, i_aref,
99
i_leftshift, i_new, i_try_convert, i_uminus, i_encode;
1010

11-
static VALUE sym_max_nesting, sym_allow_nan, sym_symbolize_names, sym_freeze,
11+
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
1212
sym_create_additions, sym_create_id, sym_object_class, sym_array_class,
1313
sym_decimal_class, sym_match_string;
1414

@@ -383,6 +383,7 @@ typedef struct JSON_ParserStruct {
383383
FBuffer fbuffer;
384384
int max_nesting;
385385
bool allow_nan;
386+
bool allow_trailing_comma;
386387
bool parsing_name;
387388
bool symbolize_names;
388389
bool freeze;
@@ -477,6 +478,8 @@ static void raise_parse_error(const char *format, const char *start)
477478
}
478479
}
479480

481+
action allow_trailing_comma { json->allow_trailing_comma }
482+
480483
action parse_name {
481484
char *np;
482485
json->parsing_name = true;
@@ -495,7 +498,7 @@ static void raise_parse_error(const char *format, const char *start)
495498

496499
main := (
497500
begin_object
498-
(pair (next_pair)*)? ignore*
501+
(pair (next_pair)*((ignore* value_separator) when allow_trailing_comma)?)? ignore*
499502
end_object
500503
) @exit;
501504
}%%
@@ -788,13 +791,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
788791
}
789792
}
790793

794+
action allow_trailing_comma { json->allow_trailing_comma }
795+
791796
action exit { fhold; fbreak; }
792797

793798
next_element = value_separator ignore* begin_value >parse_value;
794799

795800
main := begin_array ignore*
796801
((begin_value >parse_value ignore*)
797-
(ignore* next_element ignore*)*)?
802+
(ignore* next_element ignore*)*((value_separator ignore*) when allow_trailing_comma)?)?
798803
end_array @exit;
799804
}%%
800805

@@ -1073,16 +1078,17 @@ static int configure_parser_i(VALUE key, VALUE val, VALUE data)
10731078
{
10741079
JSON_Parser *json = (JSON_Parser *)data;
10751080

1076-
if (key == sym_max_nesting) { json->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
1077-
else if (key == sym_allow_nan) { json->allow_nan = RTEST(val); }
1078-
else if (key == sym_symbolize_names) { json->symbolize_names = RTEST(val); }
1079-
else if (key == sym_freeze) { json->freeze = RTEST(val); }
1080-
else if (key == sym_create_id) { json->create_id = RTEST(val) ? val : Qfalse; }
1081-
else if (key == sym_object_class) { json->object_class = RTEST(val) ? val : Qfalse; }
1082-
else if (key == sym_array_class) { json->array_class = RTEST(val) ? val : Qfalse; }
1083-
else if (key == sym_decimal_class) { json->decimal_class = RTEST(val) ? val : Qfalse; }
1084-
else if (key == sym_match_string) { json->match_string = RTEST(val) ? val : Qfalse; }
1085-
else if (key == sym_create_additions) {
1081+
if (key == sym_max_nesting) { json->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
1082+
else if (key == sym_allow_nan) { json->allow_nan = RTEST(val); }
1083+
else if (key == sym_allow_trailing_comma) { json->allow_trailing_comma = RTEST(val); }
1084+
else if (key == sym_symbolize_names) { json->symbolize_names = RTEST(val); }
1085+
else if (key == sym_freeze) { json->freeze = RTEST(val); }
1086+
else if (key == sym_create_id) { json->create_id = RTEST(val) ? val : Qfalse; }
1087+
else if (key == sym_object_class) { json->object_class = RTEST(val) ? val : Qfalse; }
1088+
else if (key == sym_array_class) { json->array_class = RTEST(val) ? val : Qfalse; }
1089+
else if (key == sym_decimal_class) { json->decimal_class = RTEST(val) ? val : Qfalse; }
1090+
else if (key == sym_match_string) { json->match_string = RTEST(val) ? val : Qfalse; }
1091+
else if (key == sym_create_additions) {
10861092
if (NIL_P(val)) {
10871093
json->create_additions = true;
10881094
json->deprecated_create_additions = true;
@@ -1358,6 +1364,7 @@ void Init_parser(void)
13581364

13591365
sym_max_nesting = ID2SYM(rb_intern("max_nesting"));
13601366
sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
1367+
sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
13611368
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
13621369
sym_freeze = ID2SYM(rb_intern("freeze"));
13631370
sym_create_additions = ID2SYM(rb_intern("create_additions"));

0 commit comments

Comments
 (0)