json 2.13.2 → 2.15.2
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 +46 -8
- data/README.md +3 -1
- data/ext/json/ext/fbuffer/fbuffer.h +31 -5
- data/ext/json/ext/generator/generator.c +134 -98
- data/ext/json/ext/parser/parser.c +9 -4
- data/ext/json/ext/vendor/fpconv.c +12 -11
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/string.rb +35 -0
- data/lib/json/common.rb +32 -7
- data/lib/json/ext/generator/state.rb +7 -14
- data/lib/json/generic_object.rb +0 -8
- data/lib/json/truffle_ruby/generator.rb +68 -46
- 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: b883349b3a8a8c1ccb003e74779a577e3a16e8c3d8541693fb477a78aeac3a68
|
|
4
|
+
data.tar.gz: efb11cf9e69ece0ebb11d33aec23401b0c1ed1d7a39c351f82998ba82cbd47b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f78e23c6bd8b8dcddaaf051d6ae60253fd6c96e108395b475d825d9daf3fba4f754f280ae095f456ee67917dd991c8477caa86038f378f23d49e17744467cb7a
|
|
7
|
+
data.tar.gz: 64946a58a223efb1e333ff15eba3776d44505cfb78ba0102350177b6351f4643107db5f37ddb65c41569b4d58b5147176c8043b182abd152c74c63351be958fd
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2025-10-25 (2.15.2)
|
|
6
|
+
|
|
7
|
+
* Fix `JSON::Coder` to have one dedicated depth counter per invocation.
|
|
8
|
+
After encountering a circular reference in `JSON::Coder#dump`, any further `#dump` call would raise `JSON::NestingError`.
|
|
9
|
+
|
|
10
|
+
### 2025-10-07 (2.15.1)
|
|
11
|
+
|
|
12
|
+
* Fix incorrect escaping in the JRuby extension when encoding shared strings.
|
|
13
|
+
|
|
14
|
+
### 2025-09-22 (2.15.0)
|
|
15
|
+
|
|
16
|
+
* `JSON::Coder` callback now receive a second argument to convey whether the object is a hash key.
|
|
17
|
+
* Tuned the floating point number generator to not use scientific notation as aggressively.
|
|
18
|
+
|
|
19
|
+
### 2025-09-18 (2.14.1)
|
|
20
|
+
|
|
21
|
+
* Fix `IndexOutOfBoundsException` in the JRuby extension when encoding shared strings.
|
|
22
|
+
|
|
23
|
+
### 2025-09-18 (2.14.0)
|
|
24
|
+
|
|
25
|
+
* Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered.
|
|
26
|
+
In `json 3.0` an error will be raised.
|
|
27
|
+
```ruby
|
|
28
|
+
>> Warning[:deprecated] = true
|
|
29
|
+
>> puts JSON.generate({ foo: 1, "foo" => 2 })
|
|
30
|
+
(irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
|
|
31
|
+
This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
|
|
32
|
+
{"foo":1,"foo":2}
|
|
33
|
+
>> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
|
|
34
|
+
detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
|
|
35
|
+
```
|
|
36
|
+
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
|
|
37
|
+
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
|
|
38
|
+
* Fix `JSON.unsafe_load` usage with proc
|
|
39
|
+
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
40
|
+
* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
|
|
41
|
+
|
|
5
42
|
### 2025-07-28 (2.13.2)
|
|
6
43
|
|
|
7
44
|
* Improve duplicate key warning and errors to include the key name and point to the right caller.
|
|
@@ -44,7 +81,7 @@
|
|
|
44
81
|
### 2025-04-24 (2.11.1)
|
|
45
82
|
|
|
46
83
|
* Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
|
|
47
|
-
These were deprecated 16 years ago, but never
|
|
84
|
+
These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are
|
|
48
85
|
still used by a few gems.
|
|
49
86
|
|
|
50
87
|
### 2025-04-24 (2.11.0)
|
|
@@ -71,7 +108,7 @@
|
|
|
71
108
|
### 2025-03-12 (2.10.2)
|
|
72
109
|
|
|
73
110
|
* Fix a potential crash in the C extension parser.
|
|
74
|
-
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0`
|
|
111
|
+
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it.
|
|
75
112
|
* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
|
|
76
113
|
* Ensure parser error snippets are valid UTF-8.
|
|
77
114
|
* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
|
|
@@ -102,7 +139,7 @@
|
|
|
102
139
|
|
|
103
140
|
### 2024-11-14 (2.8.2)
|
|
104
141
|
|
|
105
|
-
* `JSON.load_file`
|
|
142
|
+
* `JSON.load_file` explicitly read the file as UTF-8.
|
|
106
143
|
|
|
107
144
|
### 2024-11-06 (2.8.1)
|
|
108
145
|
|
|
@@ -110,7 +147,7 @@
|
|
|
110
147
|
|
|
111
148
|
### 2024-11-06 (2.8.0)
|
|
112
149
|
|
|
113
|
-
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being
|
|
150
|
+
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled.
|
|
114
151
|
* Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
|
|
115
152
|
* Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
|
|
116
153
|
* Bump required Ruby version to 2.7.
|
|
@@ -118,7 +155,7 @@
|
|
|
118
155
|
pre-existing support for comments, make it suitable to parse `jsonc` documents.
|
|
119
156
|
* Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
|
|
120
157
|
* Some minor performance improvements to `JSON.dump` and `JSON.generate`.
|
|
121
|
-
* `JSON.pretty_generate` no longer
|
|
158
|
+
* `JSON.pretty_generate` no longer includes newlines inside empty object and arrays.
|
|
122
159
|
|
|
123
160
|
### 2024-11-04 (2.7.6)
|
|
124
161
|
|
|
@@ -135,13 +172,13 @@
|
|
|
135
172
|
* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
|
|
136
173
|
This bug would cause some gems with native extension to fail during compilation.
|
|
137
174
|
* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
|
|
138
|
-
* Make `json_pure` Ractor compatible.
|
|
175
|
+
* Make `json_pure` Ractor compatible.
|
|
139
176
|
|
|
140
177
|
### 2024-10-24 (2.7.3)
|
|
141
178
|
|
|
142
179
|
* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
|
|
143
|
-
* Limit the size of ParserError exception messages, only include up to 32 bytes of the
|
|
144
|
-
* Fix json-pure's `Object#to_json` to accept non
|
|
180
|
+
* Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source.
|
|
181
|
+
* Fix json-pure's `Object#to_json` to accept non-state arguments.
|
|
145
182
|
* Fix multiline comment support in `json-pure`.
|
|
146
183
|
* Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
|
|
147
184
|
* Fix `String#to_json` to raise on invalid encoding in `json-pure`.
|
|
@@ -286,6 +323,7 @@
|
|
|
286
323
|
## 2015-09-11 (2.0.0)
|
|
287
324
|
* Now complies to newest JSON RFC 7159.
|
|
288
325
|
* Implements compatibility to ruby 2.4 integer unification.
|
|
326
|
+
* Removed support for `quirks_mode` option.
|
|
289
327
|
* Drops support for old rubies whose life has ended, that is rubies < 2.0.
|
|
290
328
|
Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
|
|
291
329
|
* There were still some mentions of dual GPL licensing in the source, but JSON
|
data/README.md
CHANGED
|
@@ -97,7 +97,7 @@ Instead it is recommended to use the newer `JSON::Coder` API:
|
|
|
97
97
|
|
|
98
98
|
```ruby
|
|
99
99
|
module MyApp
|
|
100
|
-
API_JSON_CODER = JSON::Coder.new do |object|
|
|
100
|
+
API_JSON_CODER = JSON::Coder.new do |object, is_object_key|
|
|
101
101
|
case object
|
|
102
102
|
when Time
|
|
103
103
|
object.iso8601(3)
|
|
@@ -113,6 +113,8 @@ puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
|
|
113
113
|
The provided block is called for all objects that don't have a native JSON equivalent, and
|
|
114
114
|
must return a Ruby object that has a native JSON equivalent.
|
|
115
115
|
|
|
116
|
+
It is also called for objects that do have a JSON equivalent, but are used as Hash keys, for instance `{ 1 => 2}`.
|
|
117
|
+
|
|
116
118
|
## Combining JSON fragments
|
|
117
119
|
|
|
118
120
|
To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
|
|
@@ -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,9 @@ 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
|
+
|
|
32
|
+
bool as_json_single_arg;
|
|
24
33
|
bool allow_nan;
|
|
25
34
|
bool ascii_only;
|
|
26
35
|
bool script_safe;
|
|
@@ -31,10 +40,10 @@ typedef struct JSON_Generator_StateStruct {
|
|
|
31
40
|
#define RB_UNLIKELY(cond) (cond)
|
|
32
41
|
#endif
|
|
33
42
|
|
|
34
|
-
static VALUE mJSON, cState, cFragment,
|
|
43
|
+
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
35
44
|
|
|
36
45
|
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,
|
|
46
|
+
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
47
|
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
|
|
39
48
|
|
|
40
49
|
|
|
@@ -137,8 +146,8 @@ static inline FORCE_INLINE void search_flush(search_state *search)
|
|
|
137
146
|
{
|
|
138
147
|
// Do not remove this conditional without profiling, specifically escape-heavy text.
|
|
139
148
|
// 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
|
|
149
|
+
// For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
|
|
150
|
+
// will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
|
|
142
151
|
// consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
|
|
143
152
|
// nothing needs to be flushed, we can save a few memory references with this conditional.
|
|
144
153
|
if (search->ptr > search->cursor) {
|
|
@@ -835,18 +844,6 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
835
844
|
return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
|
|
836
845
|
}
|
|
837
846
|
|
|
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
847
|
/*
|
|
851
848
|
* call-seq: to_json(*)
|
|
852
849
|
*
|
|
@@ -861,51 +858,6 @@ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
861
858
|
return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
|
|
862
859
|
}
|
|
863
860
|
|
|
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
861
|
/*
|
|
910
862
|
* call-seq: to_json(*)
|
|
911
863
|
*
|
|
@@ -1044,8 +996,11 @@ static inline VALUE vstate_get(struct generate_json_data *data)
|
|
|
1044
996
|
}
|
|
1045
997
|
|
|
1046
998
|
struct hash_foreach_arg {
|
|
999
|
+
VALUE hash;
|
|
1047
1000
|
struct generate_json_data *data;
|
|
1048
|
-
int
|
|
1001
|
+
int first_key_type;
|
|
1002
|
+
bool first;
|
|
1003
|
+
bool mixed_keys_encountered;
|
|
1049
1004
|
};
|
|
1050
1005
|
|
|
1051
1006
|
static VALUE
|
|
@@ -1063,6 +1018,29 @@ convert_string_subclass(VALUE key)
|
|
|
1063
1018
|
return key_to_s;
|
|
1064
1019
|
}
|
|
1065
1020
|
|
|
1021
|
+
NOINLINE()
|
|
1022
|
+
static void
|
|
1023
|
+
json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
|
1024
|
+
{
|
|
1025
|
+
if (arg->mixed_keys_encountered) {
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
arg->mixed_keys_encountered = true;
|
|
1029
|
+
|
|
1030
|
+
JSON_Generator_State *state = arg->data->state;
|
|
1031
|
+
if (state->on_duplicate_key != JSON_IGNORE) {
|
|
1032
|
+
VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
|
|
1033
|
+
rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
static VALUE
|
|
1038
|
+
json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
|
|
1039
|
+
{
|
|
1040
|
+
VALUE proc_args[2] = {object, is_key};
|
|
1041
|
+
return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1066
1044
|
static int
|
|
1067
1045
|
json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
1068
1046
|
{
|
|
@@ -1073,21 +1051,33 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1073
1051
|
JSON_Generator_State *state = data->state;
|
|
1074
1052
|
|
|
1075
1053
|
long depth = state->depth;
|
|
1076
|
-
int
|
|
1054
|
+
int key_type = rb_type(key);
|
|
1055
|
+
|
|
1056
|
+
if (arg->first) {
|
|
1057
|
+
arg->first = false;
|
|
1058
|
+
arg->first_key_type = key_type;
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
fbuffer_append_char(buffer, ',');
|
|
1062
|
+
}
|
|
1077
1063
|
|
|
1078
|
-
if (arg->iter > 0) fbuffer_append_char(buffer, ',');
|
|
1079
1064
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1080
1065
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1081
1066
|
}
|
|
1082
1067
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1083
|
-
|
|
1084
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1085
|
-
}
|
|
1068
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1086
1069
|
}
|
|
1087
1070
|
|
|
1088
1071
|
VALUE key_to_s;
|
|
1089
|
-
|
|
1072
|
+
bool as_json_called = false;
|
|
1073
|
+
|
|
1074
|
+
start:
|
|
1075
|
+
switch (key_type) {
|
|
1090
1076
|
case T_STRING:
|
|
1077
|
+
if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
|
|
1078
|
+
json_inspect_hash_with_mixed_keys(arg);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1091
1081
|
if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
|
|
1092
1082
|
key_to_s = key;
|
|
1093
1083
|
} else {
|
|
@@ -1095,9 +1085,23 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1095
1085
|
}
|
|
1096
1086
|
break;
|
|
1097
1087
|
case T_SYMBOL:
|
|
1088
|
+
if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
|
|
1089
|
+
json_inspect_hash_with_mixed_keys(arg);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1098
1092
|
key_to_s = rb_sym2str(key);
|
|
1099
1093
|
break;
|
|
1100
1094
|
default:
|
|
1095
|
+
if (data->state->strict) {
|
|
1096
|
+
if (RTEST(data->state->as_json) && !as_json_called) {
|
|
1097
|
+
key = json_call_as_json(data->state, key, Qtrue);
|
|
1098
|
+
key_type = rb_type(key);
|
|
1099
|
+
as_json_called = true;
|
|
1100
|
+
goto start;
|
|
1101
|
+
} else {
|
|
1102
|
+
raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1101
1105
|
key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
|
|
1102
1106
|
break;
|
|
1103
1107
|
}
|
|
@@ -1112,7 +1116,6 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1112
1116
|
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
|
|
1113
1117
|
generate_json(buffer, data, val);
|
|
1114
1118
|
|
|
1115
|
-
arg->iter++;
|
|
1116
1119
|
return ST_CONTINUE;
|
|
1117
1120
|
}
|
|
1118
1121
|
|
|
@@ -1121,14 +1124,13 @@ static inline long increase_depth(struct generate_json_data *data)
|
|
|
1121
1124
|
JSON_Generator_State *state = data->state;
|
|
1122
1125
|
long depth = ++state->depth;
|
|
1123
1126
|
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
|
1124
|
-
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
|
1127
|
+
rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --state->depth);
|
|
1125
1128
|
}
|
|
1126
1129
|
return depth;
|
|
1127
1130
|
}
|
|
1128
1131
|
|
|
1129
1132
|
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1130
1133
|
{
|
|
1131
|
-
int j;
|
|
1132
1134
|
long depth = increase_depth(data);
|
|
1133
1135
|
|
|
1134
1136
|
if (RHASH_SIZE(obj) == 0) {
|
|
@@ -1140,8 +1142,9 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1140
1142
|
fbuffer_append_char(buffer, '{');
|
|
1141
1143
|
|
|
1142
1144
|
struct hash_foreach_arg arg = {
|
|
1145
|
+
.hash = obj,
|
|
1143
1146
|
.data = data,
|
|
1144
|
-
.
|
|
1147
|
+
.first = true,
|
|
1145
1148
|
};
|
|
1146
1149
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
1147
1150
|
|
|
@@ -1149,9 +1152,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1149
1152
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1150
1153
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1151
1154
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1152
|
-
|
|
1153
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1154
|
-
}
|
|
1155
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1155
1156
|
}
|
|
1156
1157
|
}
|
|
1157
1158
|
fbuffer_append_char(buffer, '}');
|
|
@@ -1159,7 +1160,6 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1159
1160
|
|
|
1160
1161
|
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1161
1162
|
{
|
|
1162
|
-
int i, j;
|
|
1163
1163
|
long depth = increase_depth(data);
|
|
1164
1164
|
|
|
1165
1165
|
if (RARRAY_LEN(obj) == 0) {
|
|
@@ -1170,15 +1170,13 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
|
1170
1170
|
|
|
1171
1171
|
fbuffer_append_char(buffer, '[');
|
|
1172
1172
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1173
|
-
for (i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1173
|
+
for (int i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1174
1174
|
if (i > 0) {
|
|
1175
1175
|
fbuffer_append_char(buffer, ',');
|
|
1176
1176
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1177
1177
|
}
|
|
1178
1178
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1179
|
-
|
|
1180
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1181
|
-
}
|
|
1179
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1182
1180
|
}
|
|
1183
1181
|
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
|
1184
1182
|
}
|
|
@@ -1186,9 +1184,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
|
1186
1184
|
if (RB_UNLIKELY(data->state->array_nl)) {
|
|
1187
1185
|
fbuffer_append_str(buffer, data->state->array_nl);
|
|
1188
1186
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1189
|
-
|
|
1190
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1191
|
-
}
|
|
1187
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1192
1188
|
}
|
|
1193
1189
|
}
|
|
1194
1190
|
fbuffer_append_char(buffer, ']');
|
|
@@ -1340,7 +1336,7 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1340
1336
|
/* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
|
|
1341
1337
|
if (!allow_nan) {
|
|
1342
1338
|
if (data->state->strict && data->state->as_json) {
|
|
1343
|
-
VALUE casted_obj =
|
|
1339
|
+
VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1344
1340
|
if (casted_obj != obj) {
|
|
1345
1341
|
increase_depth(data);
|
|
1346
1342
|
generate_json(buffer, data, casted_obj);
|
|
@@ -1357,12 +1353,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1357
1353
|
}
|
|
1358
1354
|
|
|
1359
1355
|
/* This implementation writes directly into the buffer. We reserve
|
|
1360
|
-
* the
|
|
1356
|
+
* the 32 characters that fpconv_dtoa states as its maximum.
|
|
1361
1357
|
*/
|
|
1362
|
-
fbuffer_inc_capa(buffer,
|
|
1358
|
+
fbuffer_inc_capa(buffer, 32);
|
|
1363
1359
|
char* d = buffer->ptr + buffer->len;
|
|
1364
1360
|
int len = fpconv_dtoa(value, d);
|
|
1365
|
-
|
|
1366
1361
|
/* fpconv_dtoa converts a float to its shortest string representation,
|
|
1367
1362
|
* but it adds a ".0" if this is a plain integer.
|
|
1368
1363
|
*/
|
|
@@ -1429,7 +1424,7 @@ start:
|
|
|
1429
1424
|
general:
|
|
1430
1425
|
if (data->state->strict) {
|
|
1431
1426
|
if (RTEST(data->state->as_json) && !as_json_called) {
|
|
1432
|
-
obj =
|
|
1427
|
+
obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1433
1428
|
as_json_called = true;
|
|
1434
1429
|
goto start;
|
|
1435
1430
|
} else {
|
|
@@ -1496,10 +1491,39 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
|
1496
1491
|
rb_check_arity(argc, 1, 2);
|
|
1497
1492
|
VALUE obj = argv[0];
|
|
1498
1493
|
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1499
|
-
|
|
1494
|
+
return cState_partial_generate(self, obj, generate_json, io);
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
static VALUE cState_generate_new(int argc, VALUE *argv, VALUE self)
|
|
1498
|
+
{
|
|
1499
|
+
rb_check_arity(argc, 1, 2);
|
|
1500
|
+
VALUE obj = argv[0];
|
|
1501
|
+
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1502
|
+
|
|
1500
1503
|
GET_STATE(self);
|
|
1501
|
-
|
|
1502
|
-
|
|
1504
|
+
|
|
1505
|
+
JSON_Generator_State new_state;
|
|
1506
|
+
MEMCPY(&new_state, state, JSON_Generator_State, 1);
|
|
1507
|
+
|
|
1508
|
+
// FIXME: depth shouldn't be part of JSON_Generator_State, as that prevents it from being used concurrently.
|
|
1509
|
+
new_state.depth = 0;
|
|
1510
|
+
|
|
1511
|
+
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1512
|
+
FBuffer buffer = {
|
|
1513
|
+
.io = RTEST(io) ? io : Qfalse,
|
|
1514
|
+
};
|
|
1515
|
+
fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1516
|
+
|
|
1517
|
+
struct generate_json_data data = {
|
|
1518
|
+
.buffer = &buffer,
|
|
1519
|
+
.vstate = Qfalse,
|
|
1520
|
+
.state = &new_state,
|
|
1521
|
+
.obj = obj,
|
|
1522
|
+
.func = generate_json
|
|
1523
|
+
};
|
|
1524
|
+
rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
|
|
1525
|
+
|
|
1526
|
+
return fbuffer_finalize(&buffer);
|
|
1503
1527
|
}
|
|
1504
1528
|
|
|
1505
1529
|
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
@@ -1850,6 +1874,19 @@ static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
|
|
|
1850
1874
|
return Qnil;
|
|
1851
1875
|
}
|
|
1852
1876
|
|
|
1877
|
+
static VALUE cState_allow_duplicate_key_p(VALUE self)
|
|
1878
|
+
{
|
|
1879
|
+
GET_STATE(self);
|
|
1880
|
+
switch (state->on_duplicate_key) {
|
|
1881
|
+
case JSON_IGNORE:
|
|
1882
|
+
return Qtrue;
|
|
1883
|
+
case JSON_DEPRECATED:
|
|
1884
|
+
return Qnil;
|
|
1885
|
+
default:
|
|
1886
|
+
return Qfalse;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1853
1890
|
/*
|
|
1854
1891
|
* call-seq: depth
|
|
1855
1892
|
*
|
|
@@ -1939,8 +1976,10 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1939
1976
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
|
1940
1977
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
|
1941
1978
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
|
1979
|
+
else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
|
1942
1980
|
else if (key == sym_as_json) {
|
|
1943
1981
|
VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
|
|
1982
|
+
state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
|
|
1944
1983
|
state_write_value(data, &state->as_json, proc);
|
|
1945
1984
|
}
|
|
1946
1985
|
return ST_CONTINUE;
|
|
@@ -2062,7 +2101,9 @@ void Init_generator(void)
|
|
|
2062
2101
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
2063
2102
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
2064
2103
|
rb_define_method(cState, "generate", cState_generate, -1);
|
|
2065
|
-
|
|
2104
|
+
rb_define_method(cState, "generate_new", cState_generate_new, -1); // :nodoc:
|
|
2105
|
+
|
|
2106
|
+
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
|
2066
2107
|
|
|
2067
2108
|
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
|
2068
2109
|
|
|
@@ -2091,13 +2132,7 @@ void Init_generator(void)
|
|
|
2091
2132
|
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
|
2092
2133
|
|
|
2093
2134
|
VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
|
|
2094
|
-
rb_define_singleton_method(mString, "included", mString_included_s, 1);
|
|
2095
2135
|
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
2136
|
|
|
2102
2137
|
VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
|
|
2103
2138
|
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
|
@@ -2134,6 +2169,7 @@ void Init_generator(void)
|
|
|
2134
2169
|
sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
|
|
2135
2170
|
sym_strict = ID2SYM(rb_intern("strict"));
|
|
2136
2171
|
sym_as_json = ID2SYM(rb_intern("as_json"));
|
|
2172
|
+
sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
|
|
2137
2173
|
|
|
2138
2174
|
usascii_encindex = rb_usascii_encindex();
|
|
2139
2175
|
utf8_encindex = rb_utf8_encindex();
|
|
@@ -713,11 +713,16 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c
|
|
|
713
713
|
}
|
|
714
714
|
if (pe[0] == '\\' && pe[1] == 'u') {
|
|
715
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
|
+
|
|
716
721
|
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
|
717
722
|
| (sur & 0x3FF));
|
|
718
723
|
pe += 5;
|
|
719
724
|
} else {
|
|
720
|
-
|
|
725
|
+
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
|
721
726
|
break;
|
|
722
727
|
}
|
|
723
728
|
}
|
|
@@ -975,7 +980,7 @@ static inline bool FORCE_INLINE string_scan(JSON_ParserState *state)
|
|
|
975
980
|
if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) {
|
|
976
981
|
return 1;
|
|
977
982
|
}
|
|
978
|
-
|
|
983
|
+
state->cursor++;
|
|
979
984
|
}
|
|
980
985
|
return 0;
|
|
981
986
|
}
|
|
@@ -1265,7 +1270,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
|
1265
1270
|
break;
|
|
1266
1271
|
}
|
|
1267
1272
|
|
|
1268
|
-
raise_parse_error("
|
|
1273
|
+
raise_parse_error("unreachable: %s", state);
|
|
1269
1274
|
}
|
|
1270
1275
|
|
|
1271
1276
|
static void json_ensure_eof(JSON_ParserState *state)
|
|
@@ -1314,7 +1319,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1314
1319
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
1315
1320
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
1316
1321
|
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
|
|
1317
|
-
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; }
|
|
1318
1323
|
else if (key == sym_decimal_class) {
|
|
1319
1324
|
if (RTEST(val)) {
|
|
1320
1325
|
if (rb_respond_to(val, i_try_convert)) {
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
#include <string.h>
|
|
30
30
|
#include <stdint.h>
|
|
31
31
|
|
|
32
|
+
#ifdef JSON_DEBUG
|
|
33
|
+
#include <assert.h>
|
|
34
|
+
#endif
|
|
35
|
+
|
|
32
36
|
#define npowers 87
|
|
33
37
|
#define steppowers 8
|
|
34
38
|
#define firstpower -348 /* 10 ^ -348 */
|
|
@@ -320,15 +324,7 @@ static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg)
|
|
|
320
324
|
{
|
|
321
325
|
int exp = absv(K + ndigits - 1);
|
|
322
326
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if(neg) {
|
|
326
|
-
max_trailing_zeros -= 1;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/* write plain integer */
|
|
330
|
-
if(K >= 0 && (exp < (ndigits + max_trailing_zeros))) {
|
|
331
|
-
|
|
327
|
+
if(K >= 0 && exp < 15) {
|
|
332
328
|
memcpy(dest, digits, ndigits);
|
|
333
329
|
memset(dest + ndigits, '0', K);
|
|
334
330
|
|
|
@@ -432,10 +428,12 @@ static int filter_special(double fp, char* dest)
|
|
|
432
428
|
*
|
|
433
429
|
* Input:
|
|
434
430
|
* fp -> the double to convert, dest -> destination buffer.
|
|
435
|
-
* The generated string will never be longer than
|
|
436
|
-
* Make sure to pass a pointer to at least
|
|
431
|
+
* The generated string will never be longer than 32 characters.
|
|
432
|
+
* Make sure to pass a pointer to at least 32 bytes of memory.
|
|
437
433
|
* The emitted string will not be null terminated.
|
|
438
434
|
*
|
|
435
|
+
*
|
|
436
|
+
*
|
|
439
437
|
* Output:
|
|
440
438
|
* The number of written characters.
|
|
441
439
|
*
|
|
@@ -474,6 +472,9 @@ static int fpconv_dtoa(double d, char dest[28])
|
|
|
474
472
|
int ndigits = grisu2(d, digits, &K);
|
|
475
473
|
|
|
476
474
|
str_len += emit_digits(digits, ndigits, dest + str_len, K, neg);
|
|
475
|
+
#ifdef JSON_DEBUG
|
|
476
|
+
assert(str_len <= 32);
|
|
477
|
+
#endif
|
|
477
478
|
|
|
478
479
|
return str_len;
|
|
479
480
|
}
|
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
|
@@ -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
|
|
@@ -97,7 +97,7 @@ module JSON
|
|
|
97
97
|
|
|
98
98
|
class << self
|
|
99
99
|
def deprecation_warning(message, uplevel = 3) # :nodoc:
|
|
100
|
-
gem_root = File.expand_path("
|
|
100
|
+
gem_root = File.expand_path("..", __dir__) + "/"
|
|
101
101
|
caller_locations(uplevel, 10).each do |frame|
|
|
102
102
|
if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
|
|
103
103
|
uplevel += 1
|
|
@@ -186,6 +186,25 @@ module JSON
|
|
|
186
186
|
|
|
187
187
|
private
|
|
188
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
|
+
|
|
189
208
|
def deprecated_singleton_attr_accessor(*attrs)
|
|
190
209
|
args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
|
|
191
210
|
attrs.each do |attr|
|
|
@@ -391,7 +410,7 @@ module JSON
|
|
|
391
410
|
#
|
|
392
411
|
# Returns a \String containing the generated \JSON data.
|
|
393
412
|
#
|
|
394
|
-
# See also JSON.
|
|
413
|
+
# See also JSON.pretty_generate.
|
|
395
414
|
#
|
|
396
415
|
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
|
397
416
|
#
|
|
@@ -643,6 +662,7 @@ module JSON
|
|
|
643
662
|
# when Array
|
|
644
663
|
# obj.map! {|v| deserialize_obj v }
|
|
645
664
|
# end
|
|
665
|
+
# obj
|
|
646
666
|
# })
|
|
647
667
|
# pp ruby
|
|
648
668
|
# Output:
|
|
@@ -684,9 +704,13 @@ module JSON
|
|
|
684
704
|
if opts[:allow_blank] && (source.nil? || source.empty?)
|
|
685
705
|
source = 'null'
|
|
686
706
|
end
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
707
|
+
|
|
708
|
+
if proc
|
|
709
|
+
opts = opts.dup
|
|
710
|
+
opts[:on_load] = proc.to_proc
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
parse(source, opts)
|
|
690
714
|
end
|
|
691
715
|
|
|
692
716
|
# :call-seq:
|
|
@@ -803,6 +827,7 @@ module JSON
|
|
|
803
827
|
# when Array
|
|
804
828
|
# obj.map! {|v| deserialize_obj v }
|
|
805
829
|
# end
|
|
830
|
+
# obj
|
|
806
831
|
# })
|
|
807
832
|
# pp ruby
|
|
808
833
|
# Output:
|
|
@@ -1002,7 +1027,7 @@ module JSON
|
|
|
1002
1027
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
|
|
1003
1028
|
#
|
|
1004
1029
|
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
|
1005
|
-
#
|
|
1030
|
+
# encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
|
1006
1031
|
# \JSON counterpart:
|
|
1007
1032
|
#
|
|
1008
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
|
|
@@ -47,6 +47,14 @@ module JSON
|
|
|
47
47
|
|
|
48
48
|
SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/
|
|
49
49
|
|
|
50
|
+
def self.native_type?(value) # :nodoc:
|
|
51
|
+
(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.native_key?(key) # :nodoc:
|
|
55
|
+
(Symbol === key || String === key)
|
|
56
|
+
end
|
|
57
|
+
|
|
50
58
|
# Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
|
|
51
59
|
# UTF16 big endian characters as \u????, and return it.
|
|
52
60
|
def self.utf8_to_json(string, script_safe = false) # :nodoc:
|
|
@@ -204,7 +212,7 @@ module JSON
|
|
|
204
212
|
return if @max_nesting.zero?
|
|
205
213
|
current_nesting = depth + 1
|
|
206
214
|
current_nesting > @max_nesting and
|
|
207
|
-
raise NestingError, "nesting of #{current_nesting} is too deep"
|
|
215
|
+
raise NestingError, "nesting of #{current_nesting} is too deep. Did you try to serialize objects with circular references?"
|
|
208
216
|
end
|
|
209
217
|
|
|
210
218
|
# Returns true, if circular data structures are checked,
|
|
@@ -271,6 +279,12 @@ module JSON
|
|
|
271
279
|
false
|
|
272
280
|
end
|
|
273
281
|
|
|
282
|
+
if opts.key?(:allow_duplicate_key)
|
|
283
|
+
@allow_duplicate_key = !!opts[:allow_duplicate_key]
|
|
284
|
+
else
|
|
285
|
+
@allow_duplicate_key = nil # nil is deprecation
|
|
286
|
+
end
|
|
287
|
+
|
|
274
288
|
@strict = !!opts[:strict] if opts.key?(:strict)
|
|
275
289
|
|
|
276
290
|
if !opts.key?(:max_nesting) # defaults to 100
|
|
@@ -284,6 +298,10 @@ module JSON
|
|
|
284
298
|
end
|
|
285
299
|
alias merge configure
|
|
286
300
|
|
|
301
|
+
def allow_duplicate_key? # :nodoc:
|
|
302
|
+
@allow_duplicate_key
|
|
303
|
+
end
|
|
304
|
+
|
|
287
305
|
# Returns the configuration instance variables as a hash, that can be
|
|
288
306
|
# passed to the configure method.
|
|
289
307
|
def to_h
|
|
@@ -292,6 +310,11 @@ module JSON
|
|
|
292
310
|
iv = iv.to_s[1..-1]
|
|
293
311
|
result[iv.to_sym] = self[iv]
|
|
294
312
|
end
|
|
313
|
+
|
|
314
|
+
if result[:allow_duplicate_key].nil?
|
|
315
|
+
result.delete(:allow_duplicate_key)
|
|
316
|
+
end
|
|
317
|
+
|
|
295
318
|
result
|
|
296
319
|
end
|
|
297
320
|
|
|
@@ -324,14 +347,27 @@ module JSON
|
|
|
324
347
|
dup.generate(obj, anIO)
|
|
325
348
|
end
|
|
326
349
|
|
|
350
|
+
private def initialize_copy(_orig)
|
|
351
|
+
@depth = 0
|
|
352
|
+
end
|
|
353
|
+
|
|
327
354
|
# Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
|
|
328
355
|
private def generate_json(obj, buf)
|
|
329
356
|
case obj
|
|
330
357
|
when Hash
|
|
331
358
|
buf << '{'
|
|
332
359
|
first = true
|
|
360
|
+
key_type = nil
|
|
333
361
|
obj.each_pair do |k,v|
|
|
334
|
-
|
|
362
|
+
if first
|
|
363
|
+
key_type = k.class
|
|
364
|
+
else
|
|
365
|
+
if key_type && !@allow_duplicate_key && key_type != k.class
|
|
366
|
+
key_type = nil # stop checking
|
|
367
|
+
JSON.send(:on_mixed_keys_hash, obj, !@allow_duplicate_key.nil?)
|
|
368
|
+
end
|
|
369
|
+
buf << ','
|
|
370
|
+
end
|
|
335
371
|
|
|
336
372
|
key_str = k.to_s
|
|
337
373
|
if key_str.class == String
|
|
@@ -424,10 +460,10 @@ module JSON
|
|
|
424
460
|
state = State.from_state(state) if state
|
|
425
461
|
if state&.strict?
|
|
426
462
|
value = self
|
|
427
|
-
if state.strict? && !(
|
|
463
|
+
if state.strict? && !Generator.native_type?(value)
|
|
428
464
|
if state.as_json
|
|
429
|
-
value = state.as_json.call(value)
|
|
430
|
-
unless
|
|
465
|
+
value = state.as_json.call(value, false)
|
|
466
|
+
unless Generator.native_type?(value)
|
|
431
467
|
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
|
|
432
468
|
end
|
|
433
469
|
value.to_json(state)
|
|
@@ -471,11 +507,30 @@ module JSON
|
|
|
471
507
|
delim = ",#{state.object_nl}"
|
|
472
508
|
result = +"{#{state.object_nl}"
|
|
473
509
|
first = true
|
|
510
|
+
key_type = nil
|
|
474
511
|
indent = !state.object_nl.empty?
|
|
475
512
|
each { |key, value|
|
|
476
|
-
|
|
513
|
+
if first
|
|
514
|
+
key_type = key.class
|
|
515
|
+
else
|
|
516
|
+
if key_type && !state.allow_duplicate_key? && key_type != key.class
|
|
517
|
+
key_type = nil # stop checking
|
|
518
|
+
JSON.send(:on_mixed_keys_hash, self, state.allow_duplicate_key? == false)
|
|
519
|
+
end
|
|
520
|
+
result << delim
|
|
521
|
+
end
|
|
477
522
|
result << state.indent * depth if indent
|
|
478
523
|
|
|
524
|
+
if state.strict? && !Generator.native_key?(key)
|
|
525
|
+
if state.as_json
|
|
526
|
+
key = state.as_json.call(key, true)
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
unless Generator.native_key?(key)
|
|
530
|
+
raise GeneratorError.new("#{key.class} not allowed as object key in JSON", value)
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
|
|
479
534
|
key_str = key.to_s
|
|
480
535
|
if key_str.is_a?(String)
|
|
481
536
|
key_json = key_str.to_json(state)
|
|
@@ -484,10 +539,10 @@ module JSON
|
|
|
484
539
|
end
|
|
485
540
|
|
|
486
541
|
result = +"#{result}#{key_json}#{state.space_before}:#{state.space}"
|
|
487
|
-
if state.strict? && !(
|
|
542
|
+
if state.strict? && !Generator.native_type?(value)
|
|
488
543
|
if state.as_json
|
|
489
|
-
value = state.as_json.call(value)
|
|
490
|
-
unless
|
|
544
|
+
value = state.as_json.call(value, false)
|
|
545
|
+
unless Generator.native_type?(value)
|
|
491
546
|
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
|
|
492
547
|
end
|
|
493
548
|
result << value.to_json(state)
|
|
@@ -545,10 +600,10 @@ module JSON
|
|
|
545
600
|
each { |value|
|
|
546
601
|
result << delim unless first
|
|
547
602
|
result << state.indent * depth if indent
|
|
548
|
-
if state.strict? && !(
|
|
603
|
+
if state.strict? && !Generator.native_type?(value)
|
|
549
604
|
if state.as_json
|
|
550
|
-
value = state.as_json.call(value)
|
|
551
|
-
unless
|
|
605
|
+
value = state.as_json.call(value, false)
|
|
606
|
+
unless Generator.native_type?(value)
|
|
552
607
|
raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
|
|
553
608
|
end
|
|
554
609
|
result << value.to_json(state)
|
|
@@ -582,7 +637,7 @@ module JSON
|
|
|
582
637
|
if state.allow_nan?
|
|
583
638
|
to_s
|
|
584
639
|
elsif state.strict? && state.as_json
|
|
585
|
-
casted_value = state.as_json.call(self)
|
|
640
|
+
casted_value = state.as_json.call(self, false)
|
|
586
641
|
|
|
587
642
|
if casted_value.equal?(self)
|
|
588
643
|
raise GeneratorError.new("#{self} not allowed in JSON", self)
|
|
@@ -635,39 +690,6 @@ module JSON
|
|
|
635
690
|
rescue Encoding::UndefinedConversionError => error
|
|
636
691
|
raise ::JSON::GeneratorError.new(error.message, self)
|
|
637
692
|
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
693
|
end
|
|
672
694
|
|
|
673
695
|
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.15.2
|
|
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: []
|