json 2.13.1 → 2.14.0
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 +4 -4
- data/CHANGES.md +29 -8
- data/ext/json/ext/fbuffer/fbuffer.h +31 -5
- data/ext/json/ext/generator/generator.c +87 -88
- data/ext/json/ext/parser/parser.c +58 -8
- data/ext/json/ext/simd/simd.h +47 -46
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/string.rb +35 -0
- data/lib/json/common.rb +49 -23
- data/lib/json/ext/generator/state.rb +7 -14
- data/lib/json/generic_object.rb +0 -8
- data/lib/json/truffle_ruby/generator.rb +45 -35
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +23 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a69ef5582c620f7bfdf153a88827b4314d9d3dc01f2435a8c9ebe9d5821db91
|
4
|
+
data.tar.gz: 873e7ced8242e7cc715dbcd92c7d32970dbca037d0aa4ebf3bd37445461eb175
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62aa3ce5d1660779ddcb14d4a0b082838de6f231358d5269c5055dd7bdbe114ad01aae6626e32c333c9c2085a17eef3841b4acf318ca4b1b269f54c2bf134163
|
7
|
+
data.tar.gz: a844976bd9734865ef55ee697f5fb1a87fcbfd86bc5c5931776c98af72a597c0ee8399089d2da64957dde012e5c2a328cd75fa186ebb09811ab3d8a5479143b2
|
data/CHANGES.md
CHANGED
@@ -2,6 +2,26 @@
|
|
2
2
|
|
3
3
|
### Unreleased
|
4
4
|
|
5
|
+
* Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered.
|
6
|
+
In `json 3.0` an error will be raised.
|
7
|
+
```ruby
|
8
|
+
>> Warning[:deprecated] = true
|
9
|
+
>> puts JSON.generate({ foo: 1, "foo" => 2 })
|
10
|
+
(irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
|
11
|
+
This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
|
12
|
+
{"foo":1,"foo":2}
|
13
|
+
>> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
|
14
|
+
detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
|
15
|
+
```
|
16
|
+
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
|
17
|
+
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
|
18
|
+
* Fix `JSON.unsafe_load` usage with proc
|
19
|
+
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
20
|
+
|
21
|
+
### 2025-07-28 (2.13.2)
|
22
|
+
|
23
|
+
* Improve duplicate key warning and errors to include the key name and point to the right caller.
|
24
|
+
|
5
25
|
### 2025-07-24 (2.13.1)
|
6
26
|
|
7
27
|
* Fix support for older compilers without `__builtin_cpu_supports`.
|
@@ -40,7 +60,7 @@
|
|
40
60
|
### 2025-04-24 (2.11.1)
|
41
61
|
|
42
62
|
* Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
|
43
|
-
These were deprecated 16 years ago, but never
|
63
|
+
These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are
|
44
64
|
still used by a few gems.
|
45
65
|
|
46
66
|
### 2025-04-24 (2.11.0)
|
@@ -67,7 +87,7 @@
|
|
67
87
|
### 2025-03-12 (2.10.2)
|
68
88
|
|
69
89
|
* Fix a potential crash in the C extension parser.
|
70
|
-
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0`
|
90
|
+
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it.
|
71
91
|
* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
|
72
92
|
* Ensure parser error snippets are valid UTF-8.
|
73
93
|
* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
|
@@ -98,7 +118,7 @@
|
|
98
118
|
|
99
119
|
### 2024-11-14 (2.8.2)
|
100
120
|
|
101
|
-
* `JSON.load_file`
|
121
|
+
* `JSON.load_file` explicitly read the file as UTF-8.
|
102
122
|
|
103
123
|
### 2024-11-06 (2.8.1)
|
104
124
|
|
@@ -106,7 +126,7 @@
|
|
106
126
|
|
107
127
|
### 2024-11-06 (2.8.0)
|
108
128
|
|
109
|
-
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being
|
129
|
+
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled.
|
110
130
|
* Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
|
111
131
|
* Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
|
112
132
|
* Bump required Ruby version to 2.7.
|
@@ -114,7 +134,7 @@
|
|
114
134
|
pre-existing support for comments, make it suitable to parse `jsonc` documents.
|
115
135
|
* Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
|
116
136
|
* Some minor performance improvements to `JSON.dump` and `JSON.generate`.
|
117
|
-
* `JSON.pretty_generate` no longer
|
137
|
+
* `JSON.pretty_generate` no longer includes newlines inside empty object and arrays.
|
118
138
|
|
119
139
|
### 2024-11-04 (2.7.6)
|
120
140
|
|
@@ -131,13 +151,13 @@
|
|
131
151
|
* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
|
132
152
|
This bug would cause some gems with native extension to fail during compilation.
|
133
153
|
* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
|
134
|
-
* Make `json_pure` Ractor compatible.
|
154
|
+
* Make `json_pure` Ractor compatible.
|
135
155
|
|
136
156
|
### 2024-10-24 (2.7.3)
|
137
157
|
|
138
158
|
* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
|
139
|
-
* Limit the size of ParserError exception messages, only include up to 32 bytes of the
|
140
|
-
* Fix json-pure's `Object#to_json` to accept non
|
159
|
+
* Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source.
|
160
|
+
* Fix json-pure's `Object#to_json` to accept non-state arguments.
|
141
161
|
* Fix multiline comment support in `json-pure`.
|
142
162
|
* Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
|
143
163
|
* Fix `String#to_json` to raise on invalid encoding in `json-pure`.
|
@@ -282,6 +302,7 @@
|
|
282
302
|
## 2015-09-11 (2.0.0)
|
283
303
|
* Now complies to newest JSON RFC 7159.
|
284
304
|
* Implements compatibility to ruby 2.4 integer unification.
|
305
|
+
* Removed support for `quirks_mode` option.
|
285
306
|
* Drops support for old rubies whose life has ended, that is rubies < 2.0.
|
286
307
|
Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
|
287
308
|
* There were still some mentions of dual GPL licensing in the source, but JSON
|
@@ -24,6 +24,14 @@ typedef unsigned char _Bool;
|
|
24
24
|
#endif
|
25
25
|
#endif
|
26
26
|
|
27
|
+
#ifndef NOINLINE
|
28
|
+
#if defined(__has_attribute) && __has_attribute(noinline)
|
29
|
+
#define NOINLINE() __attribute__((noinline))
|
30
|
+
#else
|
31
|
+
#define NOINLINE()
|
32
|
+
#endif
|
33
|
+
#endif
|
34
|
+
|
27
35
|
#ifndef RB_UNLIKELY
|
28
36
|
#define RB_UNLIKELY(expr) expr
|
29
37
|
#endif
|
@@ -169,12 +177,17 @@ static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
|
169
177
|
}
|
170
178
|
}
|
171
179
|
|
172
|
-
static void
|
180
|
+
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, unsigned long len)
|
181
|
+
{
|
182
|
+
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
183
|
+
fbuffer_consumed(fb, len);
|
184
|
+
}
|
185
|
+
|
186
|
+
static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
|
173
187
|
{
|
174
188
|
if (len > 0) {
|
175
189
|
fbuffer_inc_capa(fb, len);
|
176
|
-
|
177
|
-
fbuffer_consumed(fb, len);
|
190
|
+
fbuffer_append_reserved(fb, newstr, len);
|
178
191
|
}
|
179
192
|
}
|
180
193
|
|
@@ -197,11 +210,24 @@ static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
|
197
210
|
const char *newstr = StringValuePtr(str);
|
198
211
|
unsigned long len = RSTRING_LEN(str);
|
199
212
|
|
200
|
-
RB_GC_GUARD(str);
|
201
|
-
|
202
213
|
fbuffer_append(fb, newstr, len);
|
203
214
|
}
|
204
215
|
|
216
|
+
static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
217
|
+
{
|
218
|
+
const char *newstr = StringValuePtr(str);
|
219
|
+
unsigned long len = RSTRING_LEN(str);
|
220
|
+
|
221
|
+
fbuffer_inc_capa(fb, repeat * len);
|
222
|
+
while (repeat) {
|
223
|
+
#ifdef JSON_DEBUG
|
224
|
+
fb->requested = len;
|
225
|
+
#endif
|
226
|
+
fbuffer_append_reserved(fb, newstr, len);
|
227
|
+
repeat--;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
205
231
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
206
232
|
{
|
207
233
|
fbuffer_inc_capa(fb, 1);
|
@@ -9,6 +9,12 @@
|
|
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
|
+
|
12
18
|
typedef struct JSON_Generator_StateStruct {
|
13
19
|
VALUE indent;
|
14
20
|
VALUE space;
|
@@ -21,6 +27,8 @@ typedef struct JSON_Generator_StateStruct {
|
|
21
27
|
long depth;
|
22
28
|
long buffer_initial_length;
|
23
29
|
|
30
|
+
enum duplicate_key_action on_duplicate_key;
|
31
|
+
|
24
32
|
bool allow_nan;
|
25
33
|
bool ascii_only;
|
26
34
|
bool script_safe;
|
@@ -31,10 +39,10 @@ typedef struct JSON_Generator_StateStruct {
|
|
31
39
|
#define RB_UNLIKELY(cond) (cond)
|
32
40
|
#endif
|
33
41
|
|
34
|
-
static VALUE mJSON, cState, cFragment,
|
42
|
+
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
|
35
43
|
|
36
44
|
static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
|
37
|
-
static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
|
45
|
+
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,
|
38
46
|
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
|
39
47
|
|
40
48
|
|
@@ -137,8 +145,8 @@ static inline FORCE_INLINE void search_flush(search_state *search)
|
|
137
145
|
{
|
138
146
|
// Do not remove this conditional without profiling, specifically escape-heavy text.
|
139
147
|
// escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
|
140
|
-
// For back-to-back characters that need to be escaped,
|
141
|
-
// will be called just before calling escape_UTF8_char_basic. There will be no
|
148
|
+
// For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
|
149
|
+
// will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
|
142
150
|
// consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
|
143
151
|
// nothing needs to be flushed, we can save a few memory references with this conditional.
|
144
152
|
if (search->ptr > search->cursor) {
|
@@ -835,18 +843,6 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
835
843
|
return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
|
836
844
|
}
|
837
845
|
|
838
|
-
/*
|
839
|
-
* call-seq: String.included(modul)
|
840
|
-
*
|
841
|
-
* Extends _modul_ with the String::Extend module.
|
842
|
-
*/
|
843
|
-
static VALUE mString_included_s(VALUE self, VALUE modul)
|
844
|
-
{
|
845
|
-
VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
|
846
|
-
rb_call_super(1, &modul);
|
847
|
-
return result;
|
848
|
-
}
|
849
|
-
|
850
846
|
/*
|
851
847
|
* call-seq: to_json(*)
|
852
848
|
*
|
@@ -861,51 +857,6 @@ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
|
|
861
857
|
return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
|
862
858
|
}
|
863
859
|
|
864
|
-
/*
|
865
|
-
* call-seq: to_json_raw_object()
|
866
|
-
*
|
867
|
-
* This method creates a raw object hash, that can be nested into
|
868
|
-
* other data structures and will be generated as a raw string. This
|
869
|
-
* method should be used, if you want to convert raw strings to JSON
|
870
|
-
* instead of UTF-8 strings, e. g. binary data.
|
871
|
-
*/
|
872
|
-
static VALUE mString_to_json_raw_object(VALUE self)
|
873
|
-
{
|
874
|
-
VALUE ary;
|
875
|
-
VALUE result = rb_hash_new();
|
876
|
-
rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
|
877
|
-
ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
|
878
|
-
rb_hash_aset(result, rb_utf8_str_new_lit("raw"), ary);
|
879
|
-
return result;
|
880
|
-
}
|
881
|
-
|
882
|
-
/*
|
883
|
-
* call-seq: to_json_raw(*args)
|
884
|
-
*
|
885
|
-
* This method creates a JSON text from the result of a call to
|
886
|
-
* to_json_raw_object of this String.
|
887
|
-
*/
|
888
|
-
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
|
889
|
-
{
|
890
|
-
VALUE obj = mString_to_json_raw_object(self);
|
891
|
-
Check_Type(obj, T_HASH);
|
892
|
-
return mHash_to_json(argc, argv, obj);
|
893
|
-
}
|
894
|
-
|
895
|
-
/*
|
896
|
-
* call-seq: json_create(o)
|
897
|
-
*
|
898
|
-
* Raw Strings are JSON Objects (the raw bytes are stored in an array for the
|
899
|
-
* key "raw"). The Ruby String can be created by this module method.
|
900
|
-
*/
|
901
|
-
static VALUE mString_Extend_json_create(VALUE self, VALUE o)
|
902
|
-
{
|
903
|
-
VALUE ary;
|
904
|
-
Check_Type(o, T_HASH);
|
905
|
-
ary = rb_hash_aref(o, rb_str_new2("raw"));
|
906
|
-
return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
|
907
|
-
}
|
908
|
-
|
909
860
|
/*
|
910
861
|
* call-seq: to_json(*)
|
911
862
|
*
|
@@ -1044,8 +995,11 @@ static inline VALUE vstate_get(struct generate_json_data *data)
|
|
1044
995
|
}
|
1045
996
|
|
1046
997
|
struct hash_foreach_arg {
|
998
|
+
VALUE hash;
|
1047
999
|
struct generate_json_data *data;
|
1048
|
-
int
|
1000
|
+
int first_key_type;
|
1001
|
+
bool first;
|
1002
|
+
bool mixed_keys_encountered;
|
1049
1003
|
};
|
1050
1004
|
|
1051
1005
|
static VALUE
|
@@ -1063,6 +1017,22 @@ convert_string_subclass(VALUE key)
|
|
1063
1017
|
return key_to_s;
|
1064
1018
|
}
|
1065
1019
|
|
1020
|
+
NOINLINE()
|
1021
|
+
static void
|
1022
|
+
json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
1023
|
+
{
|
1024
|
+
if (arg->mixed_keys_encountered) {
|
1025
|
+
return;
|
1026
|
+
}
|
1027
|
+
arg->mixed_keys_encountered = true;
|
1028
|
+
|
1029
|
+
JSON_Generator_State *state = arg->data->state;
|
1030
|
+
if (state->on_duplicate_key != JSON_IGNORE) {
|
1031
|
+
VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
|
1032
|
+
rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
|
1033
|
+
}
|
1034
|
+
}
|
1035
|
+
|
1066
1036
|
static int
|
1067
1037
|
json_object_i(VALUE key, VALUE val, VALUE _arg)
|
1068
1038
|
{
|
@@ -1073,21 +1043,33 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
1073
1043
|
JSON_Generator_State *state = data->state;
|
1074
1044
|
|
1075
1045
|
long depth = state->depth;
|
1076
|
-
int
|
1046
|
+
int key_type = rb_type(key);
|
1047
|
+
|
1048
|
+
if (arg->first) {
|
1049
|
+
arg->first = false;
|
1050
|
+
arg->first_key_type = key_type;
|
1051
|
+
}
|
1052
|
+
else {
|
1053
|
+
fbuffer_append_char(buffer, ',');
|
1054
|
+
}
|
1077
1055
|
|
1078
|
-
if (arg->iter > 0) fbuffer_append_char(buffer, ',');
|
1079
1056
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
1080
1057
|
fbuffer_append_str(buffer, data->state->object_nl);
|
1081
1058
|
}
|
1082
1059
|
if (RB_UNLIKELY(data->state->indent)) {
|
1083
|
-
|
1084
|
-
fbuffer_append_str(buffer, data->state->indent);
|
1085
|
-
}
|
1060
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
1086
1061
|
}
|
1087
1062
|
|
1088
1063
|
VALUE key_to_s;
|
1089
|
-
|
1064
|
+
bool as_json_called = false;
|
1065
|
+
|
1066
|
+
start:
|
1067
|
+
switch (key_type) {
|
1090
1068
|
case T_STRING:
|
1069
|
+
if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
|
1070
|
+
json_inspect_hash_with_mixed_keys(arg);
|
1071
|
+
}
|
1072
|
+
|
1091
1073
|
if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
|
1092
1074
|
key_to_s = key;
|
1093
1075
|
} else {
|
@@ -1095,9 +1077,23 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
1095
1077
|
}
|
1096
1078
|
break;
|
1097
1079
|
case T_SYMBOL:
|
1080
|
+
if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
|
1081
|
+
json_inspect_hash_with_mixed_keys(arg);
|
1082
|
+
}
|
1083
|
+
|
1098
1084
|
key_to_s = rb_sym2str(key);
|
1099
1085
|
break;
|
1100
1086
|
default:
|
1087
|
+
if (data->state->strict) {
|
1088
|
+
if (RTEST(data->state->as_json) && !as_json_called) {
|
1089
|
+
key = rb_proc_call_with_block(data->state->as_json, 1, &key, Qnil);
|
1090
|
+
key_type = rb_type(key);
|
1091
|
+
as_json_called = true;
|
1092
|
+
goto start;
|
1093
|
+
} else {
|
1094
|
+
raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
|
1095
|
+
}
|
1096
|
+
}
|
1101
1097
|
key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
|
1102
1098
|
break;
|
1103
1099
|
}
|
@@ -1112,7 +1108,6 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
1112
1108
|
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
|
1113
1109
|
generate_json(buffer, data, val);
|
1114
1110
|
|
1115
|
-
arg->iter++;
|
1116
1111
|
return ST_CONTINUE;
|
1117
1112
|
}
|
1118
1113
|
|
@@ -1128,7 +1123,6 @@ static inline long increase_depth(struct generate_json_data *data)
|
|
1128
1123
|
|
1129
1124
|
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1130
1125
|
{
|
1131
|
-
int j;
|
1132
1126
|
long depth = increase_depth(data);
|
1133
1127
|
|
1134
1128
|
if (RHASH_SIZE(obj) == 0) {
|
@@ -1140,8 +1134,9 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
1140
1134
|
fbuffer_append_char(buffer, '{');
|
1141
1135
|
|
1142
1136
|
struct hash_foreach_arg arg = {
|
1137
|
+
.hash = obj,
|
1143
1138
|
.data = data,
|
1144
|
-
.
|
1139
|
+
.first = true,
|
1145
1140
|
};
|
1146
1141
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
1147
1142
|
|
@@ -1149,9 +1144,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
1149
1144
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
1150
1145
|
fbuffer_append_str(buffer, data->state->object_nl);
|
1151
1146
|
if (RB_UNLIKELY(data->state->indent)) {
|
1152
|
-
|
1153
|
-
fbuffer_append_str(buffer, data->state->indent);
|
1154
|
-
}
|
1147
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
1155
1148
|
}
|
1156
1149
|
}
|
1157
1150
|
fbuffer_append_char(buffer, '}');
|
@@ -1159,7 +1152,6 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
1159
1152
|
|
1160
1153
|
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1161
1154
|
{
|
1162
|
-
int i, j;
|
1163
1155
|
long depth = increase_depth(data);
|
1164
1156
|
|
1165
1157
|
if (RARRAY_LEN(obj) == 0) {
|
@@ -1170,15 +1162,13 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
1170
1162
|
|
1171
1163
|
fbuffer_append_char(buffer, '[');
|
1172
1164
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
1173
|
-
for (i = 0; i < RARRAY_LEN(obj); i++) {
|
1165
|
+
for (int i = 0; i < RARRAY_LEN(obj); i++) {
|
1174
1166
|
if (i > 0) {
|
1175
1167
|
fbuffer_append_char(buffer, ',');
|
1176
1168
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
1177
1169
|
}
|
1178
1170
|
if (RB_UNLIKELY(data->state->indent)) {
|
1179
|
-
|
1180
|
-
fbuffer_append_str(buffer, data->state->indent);
|
1181
|
-
}
|
1171
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
1182
1172
|
}
|
1183
1173
|
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
1184
1174
|
}
|
@@ -1186,9 +1176,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
1186
1176
|
if (RB_UNLIKELY(data->state->array_nl)) {
|
1187
1177
|
fbuffer_append_str(buffer, data->state->array_nl);
|
1188
1178
|
if (RB_UNLIKELY(data->state->indent)) {
|
1189
|
-
|
1190
|
-
fbuffer_append_str(buffer, data->state->indent);
|
1191
|
-
}
|
1179
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
1192
1180
|
}
|
1193
1181
|
}
|
1194
1182
|
fbuffer_append_char(buffer, ']');
|
@@ -1850,6 +1838,19 @@ static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
|
|
1850
1838
|
return Qnil;
|
1851
1839
|
}
|
1852
1840
|
|
1841
|
+
static VALUE cState_allow_duplicate_key_p(VALUE self)
|
1842
|
+
{
|
1843
|
+
GET_STATE(self);
|
1844
|
+
switch (state->on_duplicate_key) {
|
1845
|
+
case JSON_IGNORE:
|
1846
|
+
return Qtrue;
|
1847
|
+
case JSON_DEPRECATED:
|
1848
|
+
return Qnil;
|
1849
|
+
default:
|
1850
|
+
return Qfalse;
|
1851
|
+
}
|
1852
|
+
}
|
1853
|
+
|
1853
1854
|
/*
|
1854
1855
|
* call-seq: depth
|
1855
1856
|
*
|
@@ -1939,6 +1940,7 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
1939
1940
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
1940
1941
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
1941
1942
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
1943
|
+
else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
1942
1944
|
else if (key == sym_as_json) {
|
1943
1945
|
VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
|
1944
1946
|
state_write_value(data, &state->as_json, proc);
|
@@ -2064,6 +2066,8 @@ void Init_generator(void)
|
|
2064
2066
|
rb_define_method(cState, "generate", cState_generate, -1);
|
2065
2067
|
rb_define_alias(cState, "generate_new", "generate"); // :nodoc:
|
2066
2068
|
|
2069
|
+
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
2070
|
+
|
2067
2071
|
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
2068
2072
|
|
2069
2073
|
VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
|
@@ -2091,13 +2095,7 @@ void Init_generator(void)
|
|
2091
2095
|
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
2092
2096
|
|
2093
2097
|
VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
|
2094
|
-
rb_define_singleton_method(mString, "included", mString_included_s, 1);
|
2095
2098
|
rb_define_method(mString, "to_json", mString_to_json, -1);
|
2096
|
-
rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
|
2097
|
-
rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
|
2098
|
-
|
2099
|
-
mString_Extend = rb_define_module_under(mString, "Extend");
|
2100
|
-
rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
|
2101
2099
|
|
2102
2100
|
VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
|
2103
2101
|
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
@@ -2134,6 +2132,7 @@ void Init_generator(void)
|
|
2134
2132
|
sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
|
2135
2133
|
sym_strict = ID2SYM(rb_intern("strict"));
|
2136
2134
|
sym_as_json = ID2SYM(rb_intern("as_json"));
|
2135
|
+
sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
|
2137
2136
|
|
2138
2137
|
usascii_encindex = rb_usascii_encindex();
|
2139
2138
|
utf8_encindex = rb_utf8_encindex();
|
@@ -422,10 +422,12 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
|
|
422
422
|
long line, column;
|
423
423
|
cursor_position(state, &line, &column);
|
424
424
|
|
425
|
-
|
425
|
+
VALUE warning = rb_sprintf("%s at line %ld column %ld", message, line, column);
|
426
|
+
rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning);
|
426
427
|
}
|
427
428
|
|
428
429
|
#define PARSE_ERROR_FRAGMENT_LEN 32
|
430
|
+
|
429
431
|
#ifdef RBIMPL_ATTR_NORETURN
|
430
432
|
RBIMPL_ATTR_NORETURN()
|
431
433
|
#endif
|
@@ -711,11 +713,16 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c
|
|
711
713
|
}
|
712
714
|
if (pe[0] == '\\' && pe[1] == 'u') {
|
713
715
|
uint32_t sur = unescape_unicode(state, (unsigned char *) pe + 2);
|
716
|
+
|
717
|
+
if ((sur & 0xFC00) != 0xDC00) {
|
718
|
+
raise_parse_error_at("invalid surrogate pair at %s", state, p);
|
719
|
+
}
|
720
|
+
|
714
721
|
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
715
722
|
| (sur & 0x3FF));
|
716
723
|
pe += 5;
|
717
724
|
} else {
|
718
|
-
|
725
|
+
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
719
726
|
break;
|
720
727
|
}
|
721
728
|
}
|
@@ -830,21 +837,64 @@ static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig
|
|
830
837
|
return array;
|
831
838
|
}
|
832
839
|
|
840
|
+
static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
|
841
|
+
{
|
842
|
+
VALUE set = rb_hash_new_capa(count / 2);
|
843
|
+
for (size_t index = 0; index < count; index += 2) {
|
844
|
+
size_t before = RHASH_SIZE(set);
|
845
|
+
VALUE key = pairs[index];
|
846
|
+
rb_hash_aset(set, key, Qtrue);
|
847
|
+
if (RHASH_SIZE(set) == before) {
|
848
|
+
if (RB_SYMBOL_P(key)) {
|
849
|
+
return rb_sym2str(key);
|
850
|
+
}
|
851
|
+
return key;
|
852
|
+
}
|
853
|
+
}
|
854
|
+
return Qfalse;
|
855
|
+
}
|
856
|
+
|
857
|
+
static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
|
858
|
+
{
|
859
|
+
VALUE message = rb_sprintf(
|
860
|
+
"detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
|
861
|
+
rb_inspect(duplicate_key)
|
862
|
+
);
|
863
|
+
|
864
|
+
emit_parse_warning(RSTRING_PTR(message), state);
|
865
|
+
RB_GC_GUARD(message);
|
866
|
+
}
|
867
|
+
|
868
|
+
#ifdef RBIMPL_ATTR_NORETURN
|
869
|
+
RBIMPL_ATTR_NORETURN()
|
870
|
+
#endif
|
871
|
+
static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
872
|
+
{
|
873
|
+
VALUE message = rb_sprintf(
|
874
|
+
"duplicate key %"PRIsVALUE,
|
875
|
+
rb_inspect(duplicate_key)
|
876
|
+
);
|
877
|
+
|
878
|
+
raise_parse_error(RSTRING_PTR(message), state);
|
879
|
+
RB_GC_GUARD(message);
|
880
|
+
}
|
881
|
+
|
833
882
|
static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count)
|
834
883
|
{
|
835
884
|
size_t entries_count = count / 2;
|
836
885
|
VALUE object = rb_hash_new_capa(entries_count);
|
837
|
-
|
886
|
+
const VALUE *pairs = rvalue_stack_peek(state->stack, count);
|
887
|
+
rb_hash_bulk_insert(count, pairs, object);
|
838
888
|
|
839
889
|
if (RB_UNLIKELY(RHASH_SIZE(object) < entries_count)) {
|
840
890
|
switch (config->on_duplicate_key) {
|
841
891
|
case JSON_IGNORE:
|
842
892
|
break;
|
843
893
|
case JSON_DEPRECATED:
|
844
|
-
|
894
|
+
emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
|
845
895
|
break;
|
846
896
|
case JSON_RAISE:
|
847
|
-
|
897
|
+
raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
|
848
898
|
break;
|
849
899
|
}
|
850
900
|
}
|
@@ -930,7 +980,7 @@ static inline bool FORCE_INLINE string_scan(JSON_ParserState *state)
|
|
930
980
|
if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) {
|
931
981
|
return 1;
|
932
982
|
}
|
933
|
-
|
983
|
+
state->cursor++;
|
934
984
|
}
|
935
985
|
return 0;
|
936
986
|
}
|
@@ -1220,7 +1270,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
1220
1270
|
break;
|
1221
1271
|
}
|
1222
1272
|
|
1223
|
-
raise_parse_error("
|
1273
|
+
raise_parse_error("unreachable: %s", state);
|
1224
1274
|
}
|
1225
1275
|
|
1226
1276
|
static void json_ensure_eof(JSON_ParserState *state)
|
@@ -1269,7 +1319,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
1269
1319
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
1270
1320
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
1271
1321
|
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
|
1272
|
-
else if (key == sym_allow_duplicate_key)
|
1322
|
+
else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
1273
1323
|
else if (key == sym_decimal_class) {
|
1274
1324
|
if (RTEST(val)) {
|
1275
1325
|
if (rb_respond_to(val, i_try_convert)) {
|
data/ext/json/ext/simd/simd.h
CHANGED
@@ -7,45 +7,45 @@ typedef enum {
|
|
7
7
|
#ifdef JSON_ENABLE_SIMD
|
8
8
|
|
9
9
|
#ifdef __clang__
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
# if __has_builtin(__builtin_ctzll)
|
11
|
+
# define HAVE_BUILTIN_CTZLL 1
|
12
|
+
# else
|
13
|
+
# define HAVE_BUILTIN_CTZLL 0
|
14
|
+
# endif
|
15
15
|
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
|
16
|
-
|
16
|
+
# define HAVE_BUILTIN_CTZLL 1
|
17
17
|
#else
|
18
|
-
|
18
|
+
# define HAVE_BUILTIN_CTZLL 0
|
19
19
|
#endif
|
20
20
|
|
21
21
|
static inline uint32_t trailing_zeros64(uint64_t input)
|
22
22
|
{
|
23
23
|
#if HAVE_BUILTIN_CTZLL
|
24
|
-
|
24
|
+
return __builtin_ctzll(input);
|
25
25
|
#else
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
uint32_t trailing_zeros = 0;
|
27
|
+
uint64_t temp = input;
|
28
|
+
while ((temp & 1) == 0 && temp > 0) {
|
29
|
+
trailing_zeros++;
|
30
|
+
temp >>= 1;
|
31
|
+
}
|
32
|
+
return trailing_zeros;
|
33
33
|
#endif
|
34
34
|
}
|
35
35
|
|
36
36
|
static inline int trailing_zeros(int input)
|
37
37
|
{
|
38
|
-
|
38
|
+
#if HAVE_BUILTIN_CTZLL
|
39
39
|
return __builtin_ctz(input);
|
40
|
-
|
40
|
+
#else
|
41
41
|
int trailing_zeros = 0;
|
42
42
|
int temp = input;
|
43
43
|
while ((temp & 1) == 0 && temp > 0) {
|
44
|
-
|
45
|
-
|
44
|
+
trailing_zeros++;
|
45
|
+
temp >>= 1;
|
46
46
|
}
|
47
47
|
return trailing_zeros;
|
48
|
-
|
48
|
+
#endif
|
49
49
|
}
|
50
50
|
|
51
51
|
#if (defined(__GNUC__ ) || defined(__clang__))
|
@@ -79,37 +79,38 @@ static inline FORCE_INLINE uint64_t neon_match_mask(uint8x16_t matches)
|
|
79
79
|
|
80
80
|
static inline FORCE_INLINE uint64_t compute_chunk_mask_neon(const char *ptr)
|
81
81
|
{
|
82
|
-
|
82
|
+
uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
// Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
|
85
|
+
// https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/
|
86
|
+
const uint8x16_t too_low_or_dbl_quote = vcltq_u8(veorq_u8(chunk, vdupq_n_u8(2)), vdupq_n_u8(33));
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
uint8x16_t has_backslash = vceqq_u8(chunk, vdupq_n_u8('\\'));
|
89
|
+
uint8x16_t needs_escape = vorrq_u8(too_low_or_dbl_quote, has_backslash);
|
90
|
+
return neon_match_mask(needs_escape);
|
91
91
|
}
|
92
92
|
|
93
93
|
static inline FORCE_INLINE int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
|
94
94
|
{
|
95
95
|
while (*ptr + sizeof(uint8x16_t) <= end) {
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
uint64_t chunk_mask = compute_chunk_mask_neon(*ptr);
|
97
|
+
if (chunk_mask) {
|
98
|
+
*mask = chunk_mask;
|
99
|
+
return 1;
|
100
|
+
}
|
101
|
+
*ptr += sizeof(uint8x16_t);
|
102
102
|
}
|
103
103
|
return 0;
|
104
104
|
}
|
105
105
|
|
106
|
-
uint8x16x4_t load_uint8x16_4(const unsigned char *table)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
106
|
+
static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
|
107
|
+
{
|
108
|
+
uint8x16x4_t tab;
|
109
|
+
tab.val[0] = vld1q_u8(table);
|
110
|
+
tab.val[1] = vld1q_u8(table+16);
|
111
|
+
tab.val[2] = vld1q_u8(table+32);
|
112
|
+
tab.val[3] = vld1q_u8(table+48);
|
113
|
+
return tab;
|
113
114
|
}
|
114
115
|
|
115
116
|
#endif /* ARM Neon Support.*/
|
@@ -150,12 +151,12 @@ static inline TARGET_SSE2 FORCE_INLINE int compute_chunk_mask_sse2(const char *p
|
|
150
151
|
static inline TARGET_SSE2 FORCE_INLINE int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
|
151
152
|
{
|
152
153
|
while (*ptr + sizeof(__m128i) <= end) {
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
154
|
+
int chunk_mask = compute_chunk_mask_sse2(*ptr);
|
155
|
+
if (chunk_mask) {
|
156
|
+
*mask = chunk_mask;
|
157
|
+
return 1;
|
158
|
+
}
|
159
|
+
*ptr += sizeof(__m128i);
|
159
160
|
}
|
160
161
|
|
161
162
|
return 0;
|
data/lib/json/add/core.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
3
|
+
require 'json'
|
4
|
+
end
|
5
|
+
|
6
|
+
class String
|
7
|
+
# call-seq: json_create(o)
|
8
|
+
#
|
9
|
+
# Raw Strings are JSON Objects (the raw bytes are stored in an array for the
|
10
|
+
# key "raw"). The Ruby String can be created by this class method.
|
11
|
+
def self.json_create(object)
|
12
|
+
object["raw"].pack("C*")
|
13
|
+
end
|
14
|
+
|
15
|
+
# call-seq: to_json_raw_object()
|
16
|
+
#
|
17
|
+
# This method creates a raw object hash, that can be nested into
|
18
|
+
# other data structures and will be generated as a raw string. This
|
19
|
+
# method should be used, if you want to convert raw strings to JSON
|
20
|
+
# instead of UTF-8 strings, e. g. binary data.
|
21
|
+
def to_json_raw_object
|
22
|
+
{
|
23
|
+
JSON.create_id => self.class.name,
|
24
|
+
"raw" => unpack("C*"),
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
# call-seq: to_json_raw(*args)
|
29
|
+
#
|
30
|
+
# This method creates a JSON text from the result of a call to
|
31
|
+
# to_json_raw_object of this String.
|
32
|
+
def to_json_raw(...)
|
33
|
+
to_json_raw_object.to_json(...)
|
34
|
+
end
|
35
|
+
end
|
data/lib/json/common.rb
CHANGED
@@ -48,7 +48,7 @@ module JSON
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
# TODO:
|
51
|
+
# TODO: extract :create_additions support to another gem for version 3.0
|
52
52
|
def create_additions_proc(opts)
|
53
53
|
if opts[:symbolize_names]
|
54
54
|
raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction"
|
@@ -73,7 +73,7 @@ module JSON
|
|
73
73
|
if opts[:create_additions] != false
|
74
74
|
if class_name = object[JSON.create_id]
|
75
75
|
klass = JSON.deep_const_get(class_name)
|
76
|
-
if
|
76
|
+
if klass.respond_to?(:json_creatable?) ? klass.json_creatable? : klass.respond_to?(:json_create)
|
77
77
|
create_additions_warning if create_additions.nil?
|
78
78
|
object = klass.json_create(object)
|
79
79
|
end
|
@@ -87,31 +87,32 @@ module JSON
|
|
87
87
|
opts
|
88
88
|
end
|
89
89
|
|
90
|
-
GEM_ROOT = File.expand_path("../../../", __FILE__) + "/"
|
91
90
|
def create_additions_warning
|
92
|
-
|
91
|
+
JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \
|
93
92
|
"and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
|
94
93
|
"pass `create_additions: true`"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
95
97
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
if RUBY_VERSION >= "3.0"
|
106
|
-
warn(message, uplevel: uplevel - 1, category: :deprecated)
|
98
|
+
class << self
|
99
|
+
def deprecation_warning(message, uplevel = 3) # :nodoc:
|
100
|
+
gem_root = File.expand_path("..", __dir__) + "/"
|
101
|
+
caller_locations(uplevel, 10).each do |frame|
|
102
|
+
if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
|
103
|
+
uplevel += 1
|
107
104
|
else
|
108
|
-
|
105
|
+
break
|
109
106
|
end
|
110
107
|
end
|
108
|
+
|
109
|
+
if RUBY_VERSION >= "3.0"
|
110
|
+
warn(message, uplevel: uplevel, category: :deprecated)
|
111
|
+
else
|
112
|
+
warn(message, uplevel: uplevel)
|
113
|
+
end
|
111
114
|
end
|
112
|
-
end
|
113
115
|
|
114
|
-
class << self
|
115
116
|
# :call-seq:
|
116
117
|
# JSON[object] -> new_array or new_string
|
117
118
|
#
|
@@ -185,6 +186,25 @@ module JSON
|
|
185
186
|
|
186
187
|
private
|
187
188
|
|
189
|
+
# Called from the extension when a hash has both string and symbol keys
|
190
|
+
def on_mixed_keys_hash(hash, do_raise)
|
191
|
+
set = {}
|
192
|
+
hash.each_key do |key|
|
193
|
+
key_str = key.to_s
|
194
|
+
|
195
|
+
if set[key_str]
|
196
|
+
message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
|
197
|
+
if do_raise
|
198
|
+
raise GeneratorError, message
|
199
|
+
else
|
200
|
+
deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`")
|
201
|
+
end
|
202
|
+
else
|
203
|
+
set[key_str] = true
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
188
208
|
def deprecated_singleton_attr_accessor(*attrs)
|
189
209
|
args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
|
190
210
|
attrs.each do |attr|
|
@@ -390,7 +410,7 @@ module JSON
|
|
390
410
|
#
|
391
411
|
# Returns a \String containing the generated \JSON data.
|
392
412
|
#
|
393
|
-
# See also JSON.
|
413
|
+
# See also JSON.pretty_generate.
|
394
414
|
#
|
395
415
|
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
396
416
|
#
|
@@ -642,6 +662,7 @@ module JSON
|
|
642
662
|
# when Array
|
643
663
|
# obj.map! {|v| deserialize_obj v }
|
644
664
|
# end
|
665
|
+
# obj
|
645
666
|
# })
|
646
667
|
# pp ruby
|
647
668
|
# Output:
|
@@ -683,9 +704,13 @@ module JSON
|
|
683
704
|
if opts[:allow_blank] && (source.nil? || source.empty?)
|
684
705
|
source = 'null'
|
685
706
|
end
|
686
|
-
|
687
|
-
|
688
|
-
|
707
|
+
|
708
|
+
if proc
|
709
|
+
opts = opts.dup
|
710
|
+
opts[:on_load] = proc.to_proc
|
711
|
+
end
|
712
|
+
|
713
|
+
parse(source, opts)
|
689
714
|
end
|
690
715
|
|
691
716
|
# :call-seq:
|
@@ -802,6 +827,7 @@ module JSON
|
|
802
827
|
# when Array
|
803
828
|
# obj.map! {|v| deserialize_obj v }
|
804
829
|
# end
|
830
|
+
# obj
|
805
831
|
# })
|
806
832
|
# pp ruby
|
807
833
|
# Output:
|
@@ -1001,7 +1027,7 @@ module JSON
|
|
1001
1027
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
|
1002
1028
|
#
|
1003
1029
|
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
1004
|
-
#
|
1030
|
+
# encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
1005
1031
|
# \JSON counterpart:
|
1006
1032
|
#
|
1007
1033
|
# module MyApp
|
@@ -8,20 +8,8 @@ module JSON
|
|
8
8
|
#
|
9
9
|
# Instantiates a new State object, configured by _opts_.
|
10
10
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# * *indent*: a string used to indent levels (default: ''),
|
14
|
-
# * *space*: a string that is put after, a : or , delimiter (default: ''),
|
15
|
-
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
|
16
|
-
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
|
17
|
-
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
|
18
|
-
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
19
|
-
# generated, otherwise an exception is thrown, if these values are
|
20
|
-
# encountered. This options defaults to false.
|
21
|
-
# * *ascii_only*: true if only ASCII characters should be generated. This
|
22
|
-
# option defaults to false.
|
23
|
-
# * *buffer_initial_length*: sets the initial length of the generator's
|
24
|
-
# internal buffer.
|
11
|
+
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
12
|
+
# See {Generating Options}[#module-JSON-label-Generating+Options].
|
25
13
|
def initialize(opts = nil)
|
26
14
|
if opts && !opts.empty?
|
27
15
|
configure(opts)
|
@@ -68,6 +56,11 @@ module JSON
|
|
68
56
|
buffer_initial_length: buffer_initial_length,
|
69
57
|
}
|
70
58
|
|
59
|
+
allow_duplicate_key = allow_duplicate_key?
|
60
|
+
unless allow_duplicate_key.nil?
|
61
|
+
result[:allow_duplicate_key] = allow_duplicate_key
|
62
|
+
end
|
63
|
+
|
71
64
|
instance_variables.each do |iv|
|
72
65
|
iv = iv.to_s[1..-1]
|
73
66
|
result[iv.to_sym] = self[iv]
|
data/lib/json/generic_object.rb
CHANGED
@@ -52,14 +52,6 @@ module JSON
|
|
52
52
|
table
|
53
53
|
end
|
54
54
|
|
55
|
-
def [](name)
|
56
|
-
__send__(name)
|
57
|
-
end unless method_defined?(:[])
|
58
|
-
|
59
|
-
def []=(name, value)
|
60
|
-
__send__("#{name}=", value)
|
61
|
-
end unless method_defined?(:[]=)
|
62
|
-
|
63
55
|
def |(other)
|
64
56
|
self.class[other.to_hash.merge(to_hash)]
|
65
57
|
end
|
@@ -271,6 +271,12 @@ module JSON
|
|
271
271
|
false
|
272
272
|
end
|
273
273
|
|
274
|
+
if opts.key?(:allow_duplicate_key)
|
275
|
+
@allow_duplicate_key = !!opts[:allow_duplicate_key]
|
276
|
+
else
|
277
|
+
@allow_duplicate_key = nil # nil is deprecation
|
278
|
+
end
|
279
|
+
|
274
280
|
@strict = !!opts[:strict] if opts.key?(:strict)
|
275
281
|
|
276
282
|
if !opts.key?(:max_nesting) # defaults to 100
|
@@ -284,6 +290,10 @@ module JSON
|
|
284
290
|
end
|
285
291
|
alias merge configure
|
286
292
|
|
293
|
+
def allow_duplicate_key? # :nodoc:
|
294
|
+
@allow_duplicate_key
|
295
|
+
end
|
296
|
+
|
287
297
|
# Returns the configuration instance variables as a hash, that can be
|
288
298
|
# passed to the configure method.
|
289
299
|
def to_h
|
@@ -292,6 +302,11 @@ module JSON
|
|
292
302
|
iv = iv.to_s[1..-1]
|
293
303
|
result[iv.to_sym] = self[iv]
|
294
304
|
end
|
305
|
+
|
306
|
+
if result[:allow_duplicate_key].nil?
|
307
|
+
result.delete(:allow_duplicate_key)
|
308
|
+
end
|
309
|
+
|
295
310
|
result
|
296
311
|
end
|
297
312
|
|
@@ -330,8 +345,17 @@ module JSON
|
|
330
345
|
when Hash
|
331
346
|
buf << '{'
|
332
347
|
first = true
|
348
|
+
key_type = nil
|
333
349
|
obj.each_pair do |k,v|
|
334
|
-
|
350
|
+
if first
|
351
|
+
key_type = k.class
|
352
|
+
else
|
353
|
+
if key_type && !@allow_duplicate_key && key_type != k.class
|
354
|
+
key_type = nil # stop checking
|
355
|
+
JSON.send(:on_mixed_keys_hash, obj, !@allow_duplicate_key.nil?)
|
356
|
+
end
|
357
|
+
buf << ','
|
358
|
+
end
|
335
359
|
|
336
360
|
key_str = k.to_s
|
337
361
|
if key_str.class == String
|
@@ -471,11 +495,30 @@ module JSON
|
|
471
495
|
delim = ",#{state.object_nl}"
|
472
496
|
result = +"{#{state.object_nl}"
|
473
497
|
first = true
|
498
|
+
key_type = nil
|
474
499
|
indent = !state.object_nl.empty?
|
475
500
|
each { |key, value|
|
476
|
-
|
501
|
+
if first
|
502
|
+
key_type = key.class
|
503
|
+
else
|
504
|
+
if key_type && !state.allow_duplicate_key? && key_type != key.class
|
505
|
+
key_type = nil # stop checking
|
506
|
+
JSON.send(:on_mixed_keys_hash, self, state.allow_duplicate_key? == false)
|
507
|
+
end
|
508
|
+
result << delim
|
509
|
+
end
|
477
510
|
result << state.indent * depth if indent
|
478
511
|
|
512
|
+
if state.strict? && !(Symbol === key || String === key)
|
513
|
+
if state.as_json
|
514
|
+
key = state.as_json.call(key)
|
515
|
+
end
|
516
|
+
|
517
|
+
unless Symbol === key || String === key
|
518
|
+
raise GeneratorError.new("#{key.class} not allowed as object key in JSON", value)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
479
522
|
key_str = key.to_s
|
480
523
|
if key_str.is_a?(String)
|
481
524
|
key_json = key_str.to_json(state)
|
@@ -635,39 +678,6 @@ module JSON
|
|
635
678
|
rescue Encoding::UndefinedConversionError => error
|
636
679
|
raise ::JSON::GeneratorError.new(error.message, self)
|
637
680
|
end
|
638
|
-
|
639
|
-
# Module that holds the extending methods if, the String module is
|
640
|
-
# included.
|
641
|
-
module Extend
|
642
|
-
# Raw Strings are JSON Objects (the raw bytes are stored in an
|
643
|
-
# array for the key "raw"). The Ruby String can be created by this
|
644
|
-
# module method.
|
645
|
-
def json_create(o)
|
646
|
-
o['raw'].pack('C*')
|
647
|
-
end
|
648
|
-
end
|
649
|
-
|
650
|
-
# Extends _modul_ with the String::Extend module.
|
651
|
-
def self.included(modul)
|
652
|
-
modul.extend Extend
|
653
|
-
end
|
654
|
-
|
655
|
-
# This method creates a raw object hash, that can be nested into
|
656
|
-
# other data structures and will be unparsed as a raw string. This
|
657
|
-
# method should be used, if you want to convert raw strings to JSON
|
658
|
-
# instead of UTF-8 strings, e. g. binary data.
|
659
|
-
def to_json_raw_object
|
660
|
-
{
|
661
|
-
JSON.create_id => self.class.name,
|
662
|
-
'raw' => self.unpack('C*'),
|
663
|
-
}
|
664
|
-
end
|
665
|
-
|
666
|
-
# This method creates a JSON text from the result of
|
667
|
-
# a call to to_json_raw_object of this String.
|
668
|
-
def to_json_raw(*args)
|
669
|
-
to_json_raw_object.to_json(*args)
|
670
|
-
end
|
671
681
|
end
|
672
682
|
|
673
683
|
module TrueClass
|
data/lib/json/version.rb
CHANGED
data/lib/json.rb
CHANGED
@@ -133,7 +133,7 @@ require 'json/common'
|
|
133
133
|
# When not specified:
|
134
134
|
# # The last value is used and a deprecation warning emitted.
|
135
135
|
# JSON.parse('{"a": 1, "a":2}') => {"a" => 2}
|
136
|
-
# #
|
136
|
+
# # warning: detected duplicate keys in JSON object.
|
137
137
|
# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
|
138
138
|
#
|
139
139
|
# When set to `+true+`
|
@@ -307,6 +307,25 @@ require 'json/common'
|
|
307
307
|
#
|
308
308
|
# ---
|
309
309
|
#
|
310
|
+
# Option +allow_duplicate_key+ (boolean) specifies whether
|
311
|
+
# hashes with duplicate keys should be allowed or produce an error.
|
312
|
+
# defaults to emit a deprecation warning.
|
313
|
+
#
|
314
|
+
# With the default, (not set):
|
315
|
+
# Warning[:deprecated] = true
|
316
|
+
# JSON.generate({ foo: 1, "foo" => 2 })
|
317
|
+
# # warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
|
318
|
+
# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
|
319
|
+
# # => '{"foo":1,"foo":2}'
|
320
|
+
#
|
321
|
+
# With <tt>false</tt>
|
322
|
+
# JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
|
323
|
+
# # detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
|
324
|
+
#
|
325
|
+
# In version 3.0, <tt>false</tt> will become the default.
|
326
|
+
#
|
327
|
+
# ---
|
328
|
+
#
|
310
329
|
# Option +max_nesting+ (\Integer) specifies the maximum nesting depth
|
311
330
|
# in +obj+; defaults to +100+.
|
312
331
|
#
|
@@ -384,6 +403,9 @@ require 'json/common'
|
|
384
403
|
#
|
385
404
|
# == \JSON Additions
|
386
405
|
#
|
406
|
+
# Note that JSON Additions must only be used with trusted data, and is
|
407
|
+
# deprecated.
|
408
|
+
#
|
387
409
|
# When you "round trip" a non-\String object from Ruby to \JSON and back,
|
388
410
|
# you have a new \String, instead of the object you began with:
|
389
411
|
# ruby0 = Range.new(0, 2)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Florian Frank
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: This is a JSON implementation as a Ruby extension in C.
|
13
13
|
email: flori@ping.de
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/json/add/rational.rb
|
46
46
|
- lib/json/add/regexp.rb
|
47
47
|
- lib/json/add/set.rb
|
48
|
+
- lib/json/add/string.rb
|
48
49
|
- lib/json/add/struct.rb
|
49
50
|
- lib/json/add/symbol.rb
|
50
51
|
- lib/json/add/time.rb
|
@@ -81,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
82
|
- !ruby/object:Gem::Version
|
82
83
|
version: '0'
|
83
84
|
requirements: []
|
84
|
-
rubygems_version: 3.6.
|
85
|
+
rubygems_version: 3.6.9
|
85
86
|
specification_version: 4
|
86
87
|
summary: JSON Implementation for Ruby
|
87
88
|
test_files: []
|