json 2.21.2 → 3.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea6afdf75f49e474e408c24daae4856891310979fbb10aa34ead66ad2fbf3321
4
- data.tar.gz: 19eb129df23cde0ac607fa6ddf909d6ea5eaef0035b2180eea4c2c8a7a5c5ff0
3
+ metadata.gz: 297c71ea2d3c5ecf3b479b5b3708cb36ff532bd700e05e7eea48c53c0e79d96f
4
+ data.tar.gz: ca9386834406f49385b0e6e091f3f9db7ef66ccfe2632b3c87d340d212872d4d
5
5
  SHA512:
6
- metadata.gz: c2212cdac41569ebd41e83e6d1ea3324e944f6a8db0070a2805f2213da7341a05cc8eab4dc6d77852f30d31a8951f36d435fdb0aff647776eb8f42cee25113ef
7
- data.tar.gz: b16fdb8a1f5384aa6e9064c7884c0f7ec96632bc2c1c6324cc2e902459e9ac94559ec691cf45dea30fc374fab74ce4e3a82f8bb9d0ec22e0ddd5357764076942
6
+ metadata.gz: 616c27a9a4ca01fce4bc13dc85da3e38bfbc8c13daa57c56396b77e61dcc215b1a6cf2994e1b7ba3c7bdbacf9c6b21ce7b7a147a057c9968d447ebe2f19039ba
7
+ data.tar.gz: 3f79549799744cfcc517fedcd722e1644fe44e8497176b5864f882174b48247e2b5c89e14f68acae527c3ee807908b20dc799daf04071acdf3ce363f235b492c
data/CHANGES.md CHANGED
@@ -2,9 +2,50 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ ### 2026-08-11 (3.0.0.rc1)
6
+
7
+ With the removal of the insecure `create_additions` option, `JSON.load` and `JSON.dump` are
8
+ now safe to use. Them being unsafe by default caused multiple security vulnerabilites in the past.
9
+
10
+ If you did depend on `create_additions`, the recommended migration is to [implement a custom serializer using
11
+ `JSON::Coder`](https://byroot.github.io/ruby/json/2025/08/02/whats-wrong-with-the-json-gem-api.html#the-create_additions-option).
12
+
13
+ All the mutable default options, such as `JSON.load_default_options` have been removed.
14
+ They were preventing Ractor compatiblity, and causing bug in libraries using JSON expecting the default behavior.
15
+ `JSON` methods now always behave the same unless monkey patched.
16
+
17
+ All methods options are now either keyword arguments or checked like keyword arguments, meaning
18
+ unknown options such as typos raise `ArgumentError`.
19
+
20
+ Duplicated keys are now rejected by default.
21
+
22
+ JavaScript comments in documents are no longer supported by default.
23
+
24
+ Numerous rarely used aliases have been removed.
25
+
26
+ * `JSON.load` defaults are now safe to use.
27
+ * All unknown options are now cause an `ArgumentError` rather than to be ignored.
28
+ * The `allow_comments` parsing option now default to `false`.
29
+ * The `allow_duplicate_key` option now defaults to `false`, for both parsing and generating JSON.
30
+ * Removed the `limit` positional argument of `JSON.dump`.
31
+ * Removed the `escape_slash` alias of `script_safe`.
32
+ * Removed `Kernel#j` and `Kernel#jj`.
33
+ * Removed `JSON.load_default_options`.
34
+ * Removed `JSON.unsafe_load_default_options`.
35
+ * Removed `JSON.dump_default_options`.
36
+ * Removed `JSON::State#[]` and `JSON::State#[]=`.
37
+ * Removed `JSON.unparse`.
38
+ * Removed `JSON.fast_generate`.
39
+ * Removed `JSON.fast_unparse`.
40
+ * Removed `JSON.pretty_unparse`.
41
+ * Removed `JSON.restore`.
42
+ * Removed `JSON::PRETTY_STATE_PROTOTYPE`.
43
+ * Removed the insecure `create_additions` option.
44
+ * Removed `JSON::GenericObject`.
45
+
5
46
  ### 2026-07-31 (2.21.2)
6
47
 
7
- * Fix a use-after-free bug in `JSON::ResumableParser`. [GHSA-9hj4-r449-hfvc].
48
+ * Fix a use-after-free bug in `JSON::ResumableParser`. [GHSA-9hj4-r449-hfvc][CVE-2026-71847].
8
49
 
9
50
  ### 2026-07-13 (2.21.1)
10
51
 
data/README.md CHANGED
@@ -14,10 +14,7 @@ UTF-16 surrogate pairs in order to be able to generate the whole range of
14
14
  unicode code points.
15
15
 
16
16
  All strings, that are to be encoded as JSON strings, should be UTF-8 byte
17
- sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8
18
- encoded, please use the to\_json\_raw\_object method of String (which produces
19
- an object, that contains a byte array) and decode the result on the receiving
20
- endpoint.
17
+ sequences on the Ruby side.
21
18
 
22
19
  ## Installation
23
20
 
@@ -49,8 +46,7 @@ JSON.generate(data)
49
46
  ```
50
47
 
51
48
  You can also use the `pretty_generate` method (which formats the output more
52
- verbosely and nicely) or `fast_generate` (which doesn't do any of the security
53
- checks generate performs, e. g. nesting deepness checks).
49
+ verbosely and nicely).
54
50
 
55
51
  ## Casting non native types
56
52
 
@@ -143,84 +139,12 @@ posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
143
139
  JSON.generate({ posts: posts_json, count: posts_json.count })
144
140
  ```
145
141
 
146
- ## Round-tripping arbitrary types
147
-
148
- > [!CAUTION]
149
- > You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
150
- > as it can lead to remote code execution vulnerabilities.
151
-
152
- To create a JSON document from a ruby data structure, you can call
153
- `JSON.generate` like that:
154
-
155
- ```ruby
156
- json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
157
- # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
158
- ```
159
-
160
- To get back a ruby data structure from a JSON document, you have to call
161
- JSON.parse on it:
162
-
163
- ```ruby
164
- JSON.parse json
165
- # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
166
- ```
167
-
168
- Note, that the range from the original data structure is a simple
169
- string now. The reason for this is, that JSON doesn't support ranges
170
- or arbitrary classes. In this case the json library falls back to call
171
- `Object#to_json`, which is the same as `#to_s.to_json`.
172
-
173
- It's possible to add JSON support serialization to arbitrary classes by
174
- simply implementing a more specialized version of the `#to_json method`, that
175
- should return a JSON object (a hash converted to JSON with `#to_json`) like
176
- this (don't forget the `*a` for all the arguments):
177
-
178
- ```ruby
179
- class Range
180
- def to_json(*a)
181
- {
182
- 'json_class' => self.class.name, # = 'Range'
183
- 'data' => [ first, last, exclude_end? ]
184
- }.to_json(*a)
185
- end
186
- end
187
- ```
188
-
189
- The hash key `json_class` is the class, that will be asked to deserialise the
190
- JSON representation later. In this case it's `Range`, but any namespace of
191
- the form `A::B` or `::A::B` will do. All other keys are arbitrary and can be
192
- used to store the necessary data to configure the object to be deserialised.
193
-
194
- If the key `json_class` is found in a JSON object, the JSON parser checks
195
- if the given class responds to the `json_create` class method. If so, it is
196
- called with the JSON object converted to a Ruby hash. So a range can
197
- be deserialised by implementing `Range.json_create` like this:
198
-
199
- ```ruby
200
- class Range
201
- def self.json_create(o)
202
- new(*o['data'])
203
- end
204
- end
205
- ```
206
-
207
- Now it possible to serialise/deserialise ranges as well:
208
-
209
- ```ruby
210
- json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
211
- # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
212
- JSON.parse json
213
- # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
214
- json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
215
- # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
216
- JSON.unsafe_load json
217
- # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
218
- ```
142
+ ## Pretty Printing
219
143
 
220
144
  `JSON.generate` always creates the shortest possible string representation of a
221
145
  ruby data structure in one line. This is good for data storage or network
222
146
  protocols, but not so good for humans to read. Fortunately there's also
223
- `JSON.pretty_generate` (or `JSON.pretty_generate`) that creates a more readable
147
+ `JSON.pretty_generate` that creates a more readable
224
148
  output:
225
149
 
226
150
  ```ruby
@@ -245,10 +169,6 @@ output:
245
169
  ]
246
170
  ```
247
171
 
248
- There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
249
- `pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
250
- the `pp` library's `pp` methods.
251
-
252
172
  ## Security
253
173
 
254
174
  When parsing or serializing untrusted input, parser and generator options should never be user controlled.
@@ -9,12 +9,6 @@
9
9
 
10
10
  /* ruby api and some helpers */
11
11
 
12
- enum duplicate_key_action {
13
- JSON_DEPRECATED = 0,
14
- JSON_IGNORE,
15
- JSON_RAISE,
16
- };
17
-
18
12
  typedef struct JSON_Generator_StateStruct {
19
13
  VALUE indent;
20
14
  VALUE space;
@@ -27,8 +21,7 @@ typedef struct JSON_Generator_StateStruct {
27
21
  long depth;
28
22
  long buffer_initial_length;
29
23
 
30
- enum duplicate_key_action on_duplicate_key;
31
-
24
+ bool allow_duplicate_key;
32
25
  bool as_json_single_arg;
33
26
  bool allow_nan;
34
27
  bool ascii_only;
@@ -41,7 +34,7 @@ static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_
41
34
 
42
35
  static ID i_to_s, i_to_json, i_new, i_encode;
43
36
  static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key,
44
- sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json, sym_sort_keys;
37
+ sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_strict, sym_as_json, sym_sort_keys;
45
38
 
46
39
 
47
40
  #define GET_STATE_TO(self, state) \
@@ -956,9 +949,8 @@ json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
956
949
  arg->mixed_keys_encountered = true;
957
950
 
958
951
  JSON_Generator_State *state = arg->data->state;
959
- if (state->on_duplicate_key != JSON_IGNORE) {
960
- VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
961
- rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
952
+ if (!state->allow_duplicate_key) {
953
+ rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 1, arg->hash);
962
954
  }
963
955
  }
964
956
 
@@ -1784,14 +1776,7 @@ static VALUE cState_sort_keys_set(VALUE self, VALUE value)
1784
1776
  static VALUE cState_allow_duplicate_key_p(VALUE self)
1785
1777
  {
1786
1778
  GET_STATE(self);
1787
- switch (state->on_duplicate_key) {
1788
- case JSON_IGNORE:
1789
- return Qtrue;
1790
- case JSON_DEPRECATED:
1791
- return Qnil;
1792
- default:
1793
- return Qfalse;
1794
- }
1779
+ return state->allow_duplicate_key ? Qtrue : Qfalse;
1795
1780
  }
1796
1781
 
1797
1782
  /*
@@ -1856,6 +1841,7 @@ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_l
1856
1841
  struct configure_state_data {
1857
1842
  JSON_Generator_State *state;
1858
1843
  VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated
1844
+ VALUE unknown_keywords;
1859
1845
  };
1860
1846
 
1861
1847
  static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value)
@@ -1883,9 +1869,8 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
1883
1869
  else if (key == sym_depth) { state->depth = depth_config(val); }
1884
1870
  else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
1885
1871
  else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
1886
- else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
1887
1872
  else if (key == sym_strict) { state->strict = RTEST(val); }
1888
- else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1873
+ else if (key == sym_allow_duplicate_key) { state->allow_duplicate_key = RTEST(val); }
1889
1874
  else if (key == sym_as_json) {
1890
1875
  VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
1891
1876
  state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
@@ -1894,6 +1879,12 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
1894
1879
  else if (key == sym_sort_keys) {
1895
1880
  state_write_value(data, &state->sort_keys, normalize_sort_keys(val));
1896
1881
  }
1882
+ else {
1883
+ if (!data->unknown_keywords) {
1884
+ data->unknown_keywords = rb_obj_hide(rb_ary_new());
1885
+ }
1886
+ rb_ary_push(data->unknown_keywords, key);
1887
+ }
1897
1888
  return ST_CONTINUE;
1898
1889
  }
1899
1890
 
@@ -1907,12 +1898,15 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
1907
1898
 
1908
1899
  struct configure_state_data data = {
1909
1900
  .state = state,
1910
- .vstate = vstate
1901
+ .vstate = vstate,
1902
+ .unknown_keywords = Qfalse,
1911
1903
  };
1912
1904
 
1913
1905
  // We assume in most cases few keys are set so it's faster to go over
1914
1906
  // the provided keys than to check all possible keys.
1915
1907
  rb_hash_foreach(config, configure_state_i, (VALUE)&data);
1908
+
1909
+ raise_argument_error_on_unknown_keywords(data.unknown_keywords);
1916
1910
  }
1917
1911
 
1918
1912
  static VALUE cState_configure(VALUE self, VALUE opts)
@@ -2006,9 +2000,6 @@ void Init_generator(void)
2006
2000
  rb_define_method(cState, "script_safe", cState_script_safe, 0);
2007
2001
  rb_define_method(cState, "script_safe?", cState_script_safe, 0);
2008
2002
  rb_define_method(cState, "script_safe=", cState_script_safe_set, 1);
2009
- rb_define_alias(cState, "escape_slash", "script_safe");
2010
- rb_define_alias(cState, "escape_slash?", "script_safe?");
2011
- rb_define_alias(cState, "escape_slash=", "script_safe=");
2012
2003
  rb_define_method(cState, "strict", cState_strict, 0);
2013
2004
  rb_define_method(cState, "strict?", cState_strict, 0);
2014
2005
  rb_define_method(cState, "strict=", cState_strict_set, 1);
@@ -2050,7 +2041,6 @@ void Init_generator(void)
2050
2041
  sym_depth = ID2SYM(rb_intern("depth"));
2051
2042
  sym_buffer_initial_length = ID2SYM(rb_intern("buffer_initial_length"));
2052
2043
  sym_script_safe = ID2SYM(rb_intern("script_safe"));
2053
- sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
2054
2044
  sym_strict = ID2SYM(rb_intern("strict"));
2055
2045
  sym_as_json = ID2SYM(rb_intern("as_json"));
2056
2046
  sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
data/ext/json/ext/json.h CHANGED
@@ -180,4 +180,17 @@ static inline VALUE json_rb_catch_obj(VALUE tag, VALUE (*func)(VALUE args), VALU
180
180
 
181
181
  #endif // JSON_TRUFFLERUBY_RB_CATCH_BUG
182
182
 
183
+ static inline void raise_argument_error_on_unknown_keywords(VALUE unknown_keywords)
184
+ {
185
+ if (RB_UNLIKELY(unknown_keywords)) {
186
+ if (RARRAY_LEN(unknown_keywords) == 1) {
187
+ rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, RARRAY_AREF(unknown_keywords, 0));
188
+ }
189
+ else {
190
+ VALUE keywords = rb_ary_join(unknown_keywords, rb_utf8_str_new_cstr(", "));
191
+ rb_raise(rb_eArgError, "unknown keywords: %" PRIsVALUE, keywords);
192
+ }
193
+ }
194
+ }
195
+
183
196
  #endif // _JSON_H_
@@ -403,19 +403,13 @@ typedef struct json_frame_stack_struct {
403
403
  json_frame *ptr;
404
404
  } json_frame_stack;
405
405
 
406
- enum deprecatable_action {
407
- JSON_DEPRECATED = 0,
408
- JSON_IGNORE,
409
- JSON_RAISE,
410
- };
411
-
412
406
  typedef struct JSON_ParserStruct {
413
407
  VALUE on_load_proc;
414
408
  VALUE decimal_class;
415
409
  ID decimal_method_id;
416
- enum deprecatable_action on_duplicate_key;
417
- enum deprecatable_action on_comment;
418
410
  int max_nesting;
411
+ bool allow_comments;
412
+ bool allow_duplicate_key;
419
413
  bool allow_nan;
420
414
  bool allow_trailing_comma;
421
415
  bool allow_control_characters;
@@ -435,7 +429,6 @@ typedef struct JSON_ParserStateStruct {
435
429
  rvalue_cache name_cache;
436
430
  int in_array;
437
431
  int current_nesting;
438
- unsigned int emitted_deprecations;
439
432
  VALUE parser;
440
433
  } JSON_ParserState;
441
434
 
@@ -619,21 +612,6 @@ static void cursor_position(JSON_ParserState *state, long *line_out, long *colum
619
612
  *column_out = column;
620
613
  }
621
614
 
622
- static const unsigned int MAX_DEPRECATIONS = 5;
623
-
624
- static void emit_parse_warning(const char *message, JSON_ParserState *state)
625
- {
626
- VALUE warning;
627
- if (state->parser) { // line and columns can't be accurate in resumable
628
- warning = rb_utf8_str_new_cstr(message);
629
- } else {
630
- long line, column;
631
- cursor_position(state, &line, &column);
632
- warning = rb_sprintf("%s at line %ld column %ld", message, line, column);
633
- }
634
- rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning);
635
- }
636
-
637
615
  #define PARSE_ERROR_FRAGMENT_LEN 32
638
616
 
639
617
  static VALUE build_parse_error_message(const char *format, JSON_ParserState *state)
@@ -772,11 +750,10 @@ static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const
772
750
 
773
751
  static const rb_data_type_t JSON_ParserConfig_type;
774
752
 
775
- const char *COMMENT_DEPRECATION_MESSAGE = "Encountered comment in JSON. This will raise an error in json 3.0 unless enabled via `allow_comments: true`";
776
753
  NOINLINE(static) void
777
754
  json_eat_comments(JSON_ParserState *state, JSON_ParserConfig *config, const char *resume_pos)
778
755
  {
779
- if (config->on_comment == JSON_RAISE) {
756
+ if (!config->allow_comments) {
780
757
  raise_syntax_error("unexpected token %s", state);
781
758
  }
782
759
 
@@ -826,11 +803,6 @@ json_eat_comments(JSON_ParserState *state, JSON_ParserConfig *config, const char
826
803
  raise_parse_error_at("unexpected token %s", state, eos(state) ? rewind_pos : start, eos(state));
827
804
  break;
828
805
  }
829
-
830
- if (config->on_comment == JSON_DEPRECATED && state->emitted_deprecations < MAX_DEPRECATIONS) {
831
- state->emitted_deprecations++;
832
- emit_parse_warning(COMMENT_DEPRECATION_MESSAGE, state);
833
- }
834
806
  }
835
807
 
836
808
  ALWAYS_INLINE(static) void
@@ -1082,7 +1054,8 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
1082
1054
  return result;
1083
1055
  }
1084
1056
 
1085
- #define MAX_FAST_INTEGER_SIZE 18
1057
+ #define MAX_FAST_INTEGER_SIZE 19
1058
+ #define MAX_FAST_UINT64_SIZE 20
1086
1059
  #define MAX_NUMBER_STACK_BUFFER 128
1087
1060
 
1088
1061
  typedef VALUE (*json_number_decode_func_t)(const char *ptr);
@@ -1117,11 +1090,30 @@ NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len)
1117
1090
 
1118
1091
  static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
1119
1092
  {
1120
- if (RB_LIKELY(mantissa_digits < MAX_FAST_INTEGER_SIZE)) {
1121
- if (negative) {
1093
+ if (RB_LIKELY(mantissa_digits <= MAX_FAST_INTEGER_SIZE)) {
1094
+ if (RB_LIKELY(!negative)) {
1095
+ return UINT64T2NUM(mantissa);
1096
+ }
1097
+
1098
+ // For a negative number 19 digits in length, we only get half of the range,
1099
+ // so ensure this negative number is less than INT64_MAX.
1100
+ //
1101
+ // Note: This does miss INT64_MIN as it's value is one past INT64_MAX
1102
+ // when converted to a uint64_t. It will still be parsed correctly by
1103
+ // falling through to json_decode_large_integer.
1104
+ if (RB_LIKELY(mantissa <= (uint64_t)INT64_MAX)) {
1122
1105
  return INT64T2NUM(-((int64_t)mantissa));
1123
1106
  }
1124
- return UINT64T2NUM(mantissa);
1107
+ }
1108
+
1109
+ if (!negative && mantissa_digits == MAX_FAST_UINT64_SIZE) {
1110
+ // Not all 20 digit integers can be safely represented by a uint64_t but
1111
+ // some can. The memcmp with uint64_max is safe as we've rejected leading
1112
+ // zeros and we have guaranteed we're comparing it with a 20 digit number.
1113
+ static const char uint64_max[] = "18446744073709551615";
1114
+ if (memcmp(end - MAX_FAST_UINT64_SIZE, uint64_max, MAX_FAST_UINT64_SIZE) <= 0) {
1115
+ return UINT64T2NUM(mantissa);
1116
+ }
1125
1117
  }
1126
1118
 
1127
1119
  return json_decode_large_integer(start, end - start);
@@ -1199,17 +1191,6 @@ static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
1199
1191
  return Qfalse;
1200
1192
  }
1201
1193
 
1202
- NOINLINE(static) void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
1203
- {
1204
- VALUE message = rb_sprintf(
1205
- "detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
1206
- rb_inspect(duplicate_key)
1207
- );
1208
-
1209
- emit_parse_warning(RSTRING_PTR(message), state);
1210
- RB_GC_GUARD(message);
1211
- }
1212
-
1213
1194
  NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
1214
1195
  {
1215
1196
  VALUE message = rb_sprintf(
@@ -1230,23 +1211,9 @@ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE d
1230
1211
 
1231
1212
  NOINLINE(static) void json_on_duplicate_key(JSON_ParserState *state, JSON_ParserConfig *config, size_t count, const VALUE *pairs)
1232
1213
  {
1233
- switch (config->on_duplicate_key) {
1234
- case JSON_IGNORE:
1235
- return;
1236
-
1237
- case JSON_DEPRECATED:
1238
- // Only emit the first few deprecations to avoid spamming.
1239
- if (state->emitted_deprecations < MAX_DEPRECATIONS) {
1240
- state->emitted_deprecations++;
1241
- emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
1242
- }
1243
- return;
1244
-
1245
- case JSON_RAISE:
1246
- raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
1247
- return;
1214
+ if (!config->allow_duplicate_key) {
1215
+ raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
1248
1216
  }
1249
- UNREACHABLE;
1250
1217
  }
1251
1218
 
1252
1219
  static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count)
@@ -2012,13 +1979,13 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
2012
1979
  if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
2013
1980
  else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
2014
1981
  else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
2015
- else if (key == sym_allow_comments) { config->on_comment = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1982
+ else if (key == sym_allow_comments) { config->allow_comments = RTEST(val); }
2016
1983
  else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
2017
1984
  else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
2018
1985
  else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
2019
1986
  else if (key == sym_freeze) { config->freeze = RTEST(val); }
2020
1987
  else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
2021
- else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1988
+ else if (key == sym_allow_duplicate_key) { config->allow_duplicate_key = RTEST(val); }
2022
1989
  else if (key == sym_decimal_class) {
2023
1990
  if (RTEST(val)) {
2024
1991
  if (rb_respond_to(val, i_try_convert)) {
@@ -2048,7 +2015,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
2048
2015
  }
2049
2016
  }
2050
2017
  }
2051
- else if (args->strict) {
2018
+ else {
2052
2019
  if (!args->unknown_keywords) {
2053
2020
  args->unknown_keywords = rb_obj_hide(rb_ary_new());
2054
2021
  }
@@ -2076,15 +2043,7 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self
2076
2043
  // the provided keys than to check all possible keys.
2077
2044
  rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
2078
2045
 
2079
- if (RB_UNLIKELY(args.unknown_keywords)) {
2080
- if (RARRAY_LEN(args.unknown_keywords) == 1) {
2081
- rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, RARRAY_AREF(args.unknown_keywords, 0));
2082
- }
2083
- else {
2084
- VALUE keywords = rb_ary_join(args.unknown_keywords, rb_utf8_str_new_cstr(", "));
2085
- rb_raise(rb_eArgError, "unknown keywords: %" PRIsVALUE, keywords);
2086
- }
2087
- }
2046
+ raise_argument_error_on_unknown_keywords(args.unknown_keywords);
2088
2047
  }
2089
2048
 
2090
2049
  /*
@@ -2646,7 +2605,6 @@ static VALUE cResumableParser_clear(VALUE self)
2646
2605
  parser->state.name_cache.length = 0;
2647
2606
  parser->state.current_nesting = 0;
2648
2607
  parser->state.in_array = 1;
2649
- parser->state.emitted_deprecations = 0;
2650
2608
  parser->state.start = parser->state.cursor = parser->state.end = NULL;
2651
2609
  return self;
2652
2610
  }