json 2.9.1 → 2.18.1
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 +167 -6
- data/LEGAL +9 -49
- data/README.md +106 -2
- data/ext/json/ext/fbuffer/fbuffer.h +104 -69
- data/ext/json/ext/generator/extconf.rb +6 -0
- data/ext/json/ext/generator/generator.c +1037 -504
- data/ext/json/ext/json.h +101 -0
- data/ext/json/ext/parser/extconf.rb +7 -3
- data/ext/json/ext/parser/parser.c +1127 -2693
- data/ext/json/ext/simd/conf.rb +24 -0
- data/ext/json/ext/simd/simd.h +218 -0
- data/ext/json/ext/vendor/fpconv.c +480 -0
- data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
- data/ext/json/ext/vendor/ryu.h +819 -0
- data/json.gemspec +4 -6
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/string.rb +35 -0
- data/lib/json/add/symbol.rb +7 -2
- data/lib/json/common.rb +432 -186
- data/lib/json/ext/generator/state.rb +12 -25
- data/lib/json/ext.rb +28 -6
- data/lib/json/generic_object.rb +0 -8
- data/lib/json/truffle_ruby/generator.rb +210 -99
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +76 -0
- metadata +13 -11
- data/ext/json/ext/parser/parser.rl +0 -1465
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cb2890db4c527125d27bc7c21fc64d3ac532ffbec8080f89a678daf48c36e09e
|
|
4
|
+
data.tar.gz: c4b37d085d05d3c43df97b3c24898dc6be61c76ba64c749b5a8a86bf4fc1198d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fb55ef5a0aa6961ef0fe3bb30f398834820357045ad27a8fdb7e53eaba3af7c4d356ef26c0e73b7a87d2d9d51e500eae7193d7d1ae3aa1058c7973bcc462674b
|
|
7
|
+
data.tar.gz: bfb499789bbcee7f5f8d67e32ded664dc62c632ae39fe80bf4bff3d6aec16eee3730a7c4883216a393326f2ab33e94e5f9c58da4c5b31627347108c36c2b211c
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,165 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
### Unreleased
|
|
4
|
+
|
|
5
|
+
### 2026-02-03 (2.18.1)
|
|
6
|
+
|
|
7
|
+
* Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
|
|
8
|
+
without first invoking a user defined `#to_json` method.
|
|
9
|
+
|
|
10
|
+
### 2025-12-11 (2.18.0)
|
|
11
|
+
|
|
12
|
+
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
13
|
+
|
|
14
|
+
### 2025-12-04 (2.17.1)
|
|
15
|
+
|
|
16
|
+
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
|
|
17
|
+
|
|
18
|
+
### 2025-12-03 (2.17.0)
|
|
19
|
+
|
|
20
|
+
* Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
|
|
21
|
+
* Fix the parser to no longer ignore invalid escapes in strings.
|
|
22
|
+
Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
|
|
23
|
+
* Fixed `JSON::Coder` to use the depth it was initialized with.
|
|
24
|
+
* On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
|
|
25
|
+
* Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
|
|
26
|
+
automatically to its initial value.
|
|
27
|
+
In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
|
|
28
|
+
generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
|
|
29
|
+
|
|
30
|
+
### 2025-11-07 (2.16.0)
|
|
31
|
+
|
|
32
|
+
* Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
|
|
33
|
+
* `JSON::Coder` now also yields to the block when encountering strings with invalid encoding.
|
|
34
|
+
* Fix GeneratorError messages to be UTF-8 encoded.
|
|
35
|
+
* Fix memory leak when `Exception` is raised, or `throw` is used during JSON generation.
|
|
36
|
+
* Optimized floating point number parsing by integrating the ryu algorithm (thanks to Josef Šimánek).
|
|
37
|
+
* Optimized numbers parsing using SWAR (thanks to Scott Myron).
|
|
38
|
+
* Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
|
|
39
|
+
|
|
40
|
+
### 2025-10-25 (2.15.2)
|
|
41
|
+
|
|
42
|
+
* Fix `JSON::Coder` to have one dedicated depth counter per invocation.
|
|
43
|
+
After encountering a circular reference in `JSON::Coder#dump`, any further `#dump` call would raise `JSON::NestingError`.
|
|
44
|
+
|
|
45
|
+
### 2025-10-07 (2.15.1)
|
|
46
|
+
|
|
47
|
+
* Fix incorrect escaping in the JRuby extension when encoding shared strings.
|
|
48
|
+
|
|
49
|
+
### 2025-09-22 (2.15.0)
|
|
50
|
+
|
|
51
|
+
* `JSON::Coder` callback now receive a second argument to convey whether the object is a hash key.
|
|
52
|
+
* Tuned the floating point number generator to not use scientific notation as aggressively.
|
|
53
|
+
|
|
54
|
+
### 2025-09-18 (2.14.1)
|
|
55
|
+
|
|
56
|
+
* Fix `IndexOutOfBoundsException` in the JRuby extension when encoding shared strings.
|
|
57
|
+
|
|
58
|
+
### 2025-09-18 (2.14.0)
|
|
59
|
+
|
|
60
|
+
* Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered.
|
|
61
|
+
In `json 3.0` an error will be raised.
|
|
62
|
+
```ruby
|
|
63
|
+
>> Warning[:deprecated] = true
|
|
64
|
+
>> puts JSON.generate({ foo: 1, "foo" => 2 })
|
|
65
|
+
(irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
|
|
66
|
+
This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
|
|
67
|
+
{"foo":1,"foo":2}
|
|
68
|
+
>> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
|
|
69
|
+
detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
|
|
70
|
+
```
|
|
71
|
+
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
|
|
72
|
+
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
|
|
73
|
+
* Fix `JSON.unsafe_load` usage with proc
|
|
74
|
+
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
75
|
+
* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
|
|
76
|
+
|
|
77
|
+
### 2025-07-28 (2.13.2)
|
|
78
|
+
|
|
79
|
+
* Improve duplicate key warning and errors to include the key name and point to the right caller.
|
|
80
|
+
|
|
81
|
+
### 2025-07-24 (2.13.1)
|
|
82
|
+
|
|
83
|
+
* Fix support for older compilers without `__builtin_cpu_supports`.
|
|
84
|
+
|
|
85
|
+
### 2025-07-17 (2.13.0)
|
|
86
|
+
|
|
87
|
+
* Add new `allow_duplicate_key` parsing options. By default a warning is now emitted when a duplicated key is encountered.
|
|
88
|
+
In `json 3.0` an error will be raised.
|
|
89
|
+
* Optimize parsing further using SIMD to scan strings.
|
|
90
|
+
|
|
91
|
+
### 2025-05-23 (2.12.2)
|
|
92
|
+
|
|
93
|
+
* Fix compiler optimization level.
|
|
94
|
+
|
|
95
|
+
### 2025-05-23 (2.12.1)
|
|
96
|
+
|
|
97
|
+
* Fix a potential crash in large negative floating point number generation.
|
|
98
|
+
* Fix for JSON.pretty_generate to use passed state object's generate instead of state class as the required parameters aren't available.
|
|
99
|
+
|
|
100
|
+
### 2025-05-12 (2.12.0)
|
|
101
|
+
|
|
102
|
+
* Improve floating point generation to not use scientific notation as much.
|
|
103
|
+
* Include line and column in parser errors. Both in the message and as exception attributes.
|
|
104
|
+
* Handle non-string hash keys with broken `to_s` implementations.
|
|
105
|
+
* `JSON.generate` now uses SSE2 (x86) or NEON (arm64) instructions when available to escape strings.
|
|
106
|
+
|
|
107
|
+
### 2025-04-25 (2.11.3)
|
|
108
|
+
|
|
109
|
+
* Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
|
|
110
|
+
|
|
111
|
+
### 2025-04-24 (2.11.2)
|
|
112
|
+
|
|
113
|
+
* Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
|
|
114
|
+
It now emits a deprecation warning.
|
|
115
|
+
|
|
116
|
+
### 2025-04-24 (2.11.1)
|
|
117
|
+
|
|
118
|
+
* Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
|
|
119
|
+
These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are
|
|
120
|
+
still used by a few gems.
|
|
121
|
+
|
|
122
|
+
### 2025-04-24 (2.11.0)
|
|
123
|
+
|
|
124
|
+
* Optimize Integer generation to be ~1.8x faster.
|
|
125
|
+
* Optimize Float generation to be ~10x faster.
|
|
126
|
+
* Fix `JSON.load` proc argument to substitute the parsed object with the return value.
|
|
127
|
+
This better match `Marshal.load` behavior.
|
|
128
|
+
* Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
|
|
129
|
+
* Deprecate `JSON.load_default_options`.
|
|
130
|
+
* Deprecate `JSON.unsafe_load_default_options`.
|
|
131
|
+
* Deprecate `JSON.dump_default_options`.
|
|
132
|
+
* Deprecate `Kernel#j`
|
|
133
|
+
* Deprecate `Kernel#jj`
|
|
134
|
+
* Remove outdated `JSON.iconv`.
|
|
135
|
+
* Remove `Class#json_creatable?` monkey patch.
|
|
136
|
+
* Remove deprecated `JSON.restore` method.
|
|
137
|
+
* Remove deprecated `JSON.unparse` method.
|
|
138
|
+
* Remove deprecated `JSON.fast_unparse` method.
|
|
139
|
+
* Remove deprecated `JSON.pretty_unparse` method.
|
|
140
|
+
* Remove deprecated `JSON::UnparserError` constant.
|
|
141
|
+
* Remove outdated `JSON::MissingUnicodeSupport` constant.
|
|
142
|
+
|
|
143
|
+
### 2025-03-12 (2.10.2)
|
|
144
|
+
|
|
145
|
+
* Fix a potential crash in the C extension parser.
|
|
146
|
+
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it.
|
|
147
|
+
* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
|
|
148
|
+
* Ensure parser error snippets are valid UTF-8.
|
|
149
|
+
* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
|
|
150
|
+
|
|
151
|
+
### 2025-02-10 (2.10.1)
|
|
152
|
+
|
|
153
|
+
* Fix a compatibility issue with `MultiJson.dump(obj, pretty: true)`: `no implicit conversion of false into Proc (TypeError)`.
|
|
154
|
+
|
|
155
|
+
### 2025-02-10 (2.10.0)
|
|
156
|
+
|
|
157
|
+
* `strict: true` now accept symbols as values. Previously they'd only be accepted as hash keys.
|
|
158
|
+
* The C extension Parser has been entirely reimplemented from scratch.
|
|
159
|
+
* Introduced `JSON::Coder` as a new API allowing to customize how non native types are serialized in a non-global way.
|
|
160
|
+
* Introduced `JSON::Fragment` to allow assembling cached fragments in a safe way.
|
|
161
|
+
* The Java implementation of the generator received many optimizations.
|
|
162
|
+
|
|
3
163
|
### 2024-12-18 (2.9.1)
|
|
4
164
|
|
|
5
165
|
* Fix support for Solaris 10.
|
|
@@ -14,7 +174,7 @@
|
|
|
14
174
|
|
|
15
175
|
### 2024-11-14 (2.8.2)
|
|
16
176
|
|
|
17
|
-
* `JSON.load_file`
|
|
177
|
+
* `JSON.load_file` explicitly read the file as UTF-8.
|
|
18
178
|
|
|
19
179
|
### 2024-11-06 (2.8.1)
|
|
20
180
|
|
|
@@ -22,7 +182,7 @@
|
|
|
22
182
|
|
|
23
183
|
### 2024-11-06 (2.8.0)
|
|
24
184
|
|
|
25
|
-
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being
|
|
185
|
+
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled.
|
|
26
186
|
* Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
|
|
27
187
|
* Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
|
|
28
188
|
* Bump required Ruby version to 2.7.
|
|
@@ -30,7 +190,7 @@
|
|
|
30
190
|
pre-existing support for comments, make it suitable to parse `jsonc` documents.
|
|
31
191
|
* Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
|
|
32
192
|
* Some minor performance improvements to `JSON.dump` and `JSON.generate`.
|
|
33
|
-
* `JSON.pretty_generate` no longer
|
|
193
|
+
* `JSON.pretty_generate` no longer includes newlines inside empty object and arrays.
|
|
34
194
|
|
|
35
195
|
### 2024-11-04 (2.7.6)
|
|
36
196
|
|
|
@@ -47,13 +207,13 @@
|
|
|
47
207
|
* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
|
|
48
208
|
This bug would cause some gems with native extension to fail during compilation.
|
|
49
209
|
* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
|
|
50
|
-
* Make `json_pure` Ractor compatible.
|
|
210
|
+
* Make `json_pure` Ractor compatible.
|
|
51
211
|
|
|
52
212
|
### 2024-10-24 (2.7.3)
|
|
53
213
|
|
|
54
214
|
* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
|
|
55
|
-
* Limit the size of ParserError exception messages, only include up to 32 bytes of the
|
|
56
|
-
* Fix json-pure's `Object#to_json` to accept non
|
|
215
|
+
* Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source.
|
|
216
|
+
* Fix json-pure's `Object#to_json` to accept non-state arguments.
|
|
57
217
|
* Fix multiline comment support in `json-pure`.
|
|
58
218
|
* Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
|
|
59
219
|
* Fix `String#to_json` to raise on invalid encoding in `json-pure`.
|
|
@@ -198,6 +358,7 @@
|
|
|
198
358
|
## 2015-09-11 (2.0.0)
|
|
199
359
|
* Now complies to newest JSON RFC 7159.
|
|
200
360
|
* Implements compatibility to ruby 2.4 integer unification.
|
|
361
|
+
* Removed support for `quirks_mode` option.
|
|
201
362
|
* Drops support for old rubies whose life has ended, that is rubies < 2.0.
|
|
202
363
|
Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
|
|
203
364
|
* There were still some mentions of dual GPL licensing in the source, but JSON
|
data/LEGAL
CHANGED
|
@@ -7,54 +7,14 @@ All the files in this distribution are covered under either the Ruby's
|
|
|
7
7
|
license (see the file COPYING) or public-domain except some files
|
|
8
8
|
mentioned below.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
a copy of this software and associated documentation files (the
|
|
14
|
-
"Software"), to deal in the Software without restriction, including
|
|
15
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
|
16
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
|
17
|
-
permit persons to whom the Software is furnished to do so, subject to
|
|
18
|
-
the following conditions:
|
|
10
|
+
ext/json/ext/vendor/fpconv.h::
|
|
11
|
+
This file is adapted from https://github.com/night-shift/fpconv
|
|
12
|
+
It is licensed under Boost Software License 1.0.
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
ext/json/ext/vendor/jeaiii-ltoa.h::
|
|
15
|
+
This file is adapted from https://github.com/jeaiii/itoa
|
|
16
|
+
It is licensed under the MIT License
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
27
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
28
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
29
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
30
|
-
|
|
31
|
-
== Old-style BSD license
|
|
32
|
-
>>>
|
|
33
|
-
Redistribution and use in source and binary forms, with or without
|
|
34
|
-
modification, are permitted provided that the following conditions
|
|
35
|
-
are met:
|
|
36
|
-
1. Redistributions of source code must retain the above copyright
|
|
37
|
-
notice, this list of conditions and the following disclaimer.
|
|
38
|
-
2. Redistributions in binary form must reproduce the above copyright
|
|
39
|
-
notice, this list of conditions and the following disclaimer in the
|
|
40
|
-
documentation and/or other materials provided with the distribution.
|
|
41
|
-
3. Neither the name of the University nor the names of its contributors
|
|
42
|
-
may be used to endorse or promote products derived from this software
|
|
43
|
-
without specific prior written permission.
|
|
44
|
-
|
|
45
|
-
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
46
|
-
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
47
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
48
|
-
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
49
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
50
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
51
|
-
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
52
|
-
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
53
|
-
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
54
|
-
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
55
|
-
SUCH DAMAGE.
|
|
56
|
-
|
|
57
|
-
IMPORTANT NOTE::
|
|
58
|
-
|
|
59
|
-
From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
|
|
60
|
-
paragraph 3 above is now null and void.
|
|
18
|
+
ext/json/ext/vendor/ryu.h::
|
|
19
|
+
This file is adapted from the Ryu algorithm by Ulf Adams https://github.com/ulfjack/ryu.
|
|
20
|
+
It is dual-licensed under Apache License 2.0 OR Boost Software License 1.0.
|
data/README.md
CHANGED
|
@@ -29,7 +29,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
|
29
29
|
|
|
30
30
|
$ gem install json
|
|
31
31
|
|
|
32
|
-
## Usage
|
|
32
|
+
## Basic Usage
|
|
33
33
|
|
|
34
34
|
To use JSON you can
|
|
35
35
|
|
|
@@ -52,7 +52,98 @@ You can also use the `pretty_generate` method (which formats the output more
|
|
|
52
52
|
verbosely and nicely) or `fast_generate` (which doesn't do any of the security
|
|
53
53
|
checks generate performs, e. g. nesting deepness checks).
|
|
54
54
|
|
|
55
|
-
##
|
|
55
|
+
## Casting non native types
|
|
56
|
+
|
|
57
|
+
JSON documents can only support Hashes, Arrays, Strings, Integers and Floats.
|
|
58
|
+
|
|
59
|
+
By default if you attempt to serialize something else, `JSON.generate` will
|
|
60
|
+
search for a `#to_json` method on that object:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
Position = Struct.new(:latitude, :longitude) do
|
|
64
|
+
def to_json(state = nil, *)
|
|
65
|
+
JSON::State.from_state(state).generate({
|
|
66
|
+
latitude: latitude,
|
|
67
|
+
longitude: longitude,
|
|
68
|
+
})
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
JSON.generate([
|
|
73
|
+
Position.new(12323.234, 435345.233),
|
|
74
|
+
Position.new(23434.676, 159435.324),
|
|
75
|
+
]) # => [{"latitude":12323.234,"longitude":435345.233},{"latitude":23434.676,"longitude":159435.324}]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If a `#to_json` method isn't defined on the object, `JSON.generate` will fallback to call `#to_s`:
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
JSON.generate(Object.new) # => "#<Object:0x000000011e768b98>"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Both of these behavior can be disabled using the `strict: true` option:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError)
|
|
88
|
+
JSON.generate(Position.new(1, 2)) # => Position not allowed in JSON (JSON::GeneratorError)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## JSON::Coder
|
|
92
|
+
|
|
93
|
+
Since `#to_json` methods are global, it can sometimes be problematic if you need a given type to be
|
|
94
|
+
serialized in different ways in different locations.
|
|
95
|
+
|
|
96
|
+
Instead it is recommended to use the newer `JSON::Coder` API:
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
module MyApp
|
|
100
|
+
API_JSON_CODER = JSON::Coder.new do |object, is_object_key|
|
|
101
|
+
case object
|
|
102
|
+
when Time
|
|
103
|
+
object.iso8601(3)
|
|
104
|
+
else
|
|
105
|
+
object
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The provided block is called for all objects that don't have a native JSON equivalent, and
|
|
114
|
+
must return a Ruby object that has a native JSON equivalent.
|
|
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
|
+
as well as for strings that aren't valid UTF-8:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
coder = JSON::Combining.new do |object, is_object_key|
|
|
121
|
+
case object
|
|
122
|
+
when String
|
|
123
|
+
if !string.valid_encoding? || string.encoding != Encoding::UTF_8
|
|
124
|
+
Base64.encode64(string)
|
|
125
|
+
else
|
|
126
|
+
string
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
object
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Combining JSON fragments
|
|
135
|
+
|
|
136
|
+
To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
posts_json = cache.fetch_multi(post_ids) do |post_id|
|
|
140
|
+
JSON.generate(Post.find(post_id))
|
|
141
|
+
end
|
|
142
|
+
posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
|
|
143
|
+
JSON.generate({ posts: posts_json, count: posts_json.count })
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Round-tripping arbitrary types
|
|
56
147
|
|
|
57
148
|
> [!CAUTION]
|
|
58
149
|
> You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
|
|
@@ -160,6 +251,19 @@ the `pp` library's `pp` methods.
|
|
|
160
251
|
|
|
161
252
|
## Development
|
|
162
253
|
|
|
254
|
+
### Prerequisites
|
|
255
|
+
|
|
256
|
+
1. Clone the repository
|
|
257
|
+
2. Install dependencies with `bundle install`
|
|
258
|
+
|
|
259
|
+
### Testing
|
|
260
|
+
|
|
261
|
+
The full test suite can be run with:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
bundle exec rake test
|
|
265
|
+
```
|
|
266
|
+
|
|
163
267
|
### Release
|
|
164
268
|
|
|
165
269
|
Update the `lib/json/version.rb` file.
|
|
@@ -1,39 +1,8 @@
|
|
|
1
1
|
#ifndef _FBUFFER_H_
|
|
2
2
|
#define _FBUFFER_H_
|
|
3
3
|
|
|
4
|
-
#include "
|
|
5
|
-
#include "
|
|
6
|
-
|
|
7
|
-
/* shims */
|
|
8
|
-
/* This is the fallback definition from Ruby 3.4 */
|
|
9
|
-
|
|
10
|
-
#ifndef RBIMPL_STDBOOL_H
|
|
11
|
-
#if defined(__cplusplus)
|
|
12
|
-
# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
|
|
13
|
-
# include <cstdbool>
|
|
14
|
-
# endif
|
|
15
|
-
#elif defined(HAVE_STDBOOL_H)
|
|
16
|
-
# include <stdbool.h>
|
|
17
|
-
#elif !defined(HAVE__BOOL)
|
|
18
|
-
typedef unsigned char _Bool;
|
|
19
|
-
# define bool _Bool
|
|
20
|
-
# define true ((_Bool)+1)
|
|
21
|
-
# define false ((_Bool)+0)
|
|
22
|
-
# define __bool_true_false_are_defined
|
|
23
|
-
#endif
|
|
24
|
-
#endif
|
|
25
|
-
|
|
26
|
-
#ifndef RB_UNLIKELY
|
|
27
|
-
#define RB_UNLIKELY(expr) expr
|
|
28
|
-
#endif
|
|
29
|
-
|
|
30
|
-
#ifndef RB_LIKELY
|
|
31
|
-
#define RB_LIKELY(expr) expr
|
|
32
|
-
#endif
|
|
33
|
-
|
|
34
|
-
#ifndef MAYBE_UNUSED
|
|
35
|
-
# define MAYBE_UNUSED(x) x
|
|
36
|
-
#endif
|
|
4
|
+
#include "../json.h"
|
|
5
|
+
#include "../vendor/jeaiii-ltoa.h"
|
|
37
6
|
|
|
38
7
|
enum fbuffer_type {
|
|
39
8
|
FBUFFER_HEAP_ALLOCATED = 0,
|
|
@@ -45,6 +14,9 @@ typedef struct FBufferStruct {
|
|
|
45
14
|
unsigned long initial_length;
|
|
46
15
|
unsigned long len;
|
|
47
16
|
unsigned long capa;
|
|
17
|
+
#if JSON_DEBUG
|
|
18
|
+
unsigned long requested;
|
|
19
|
+
#endif
|
|
48
20
|
char *ptr;
|
|
49
21
|
VALUE io;
|
|
50
22
|
} FBuffer;
|
|
@@ -59,17 +31,11 @@ typedef struct FBufferStruct {
|
|
|
59
31
|
#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
|
|
60
32
|
|
|
61
33
|
static void fbuffer_free(FBuffer *fb);
|
|
62
|
-
#ifndef JSON_GENERATOR
|
|
63
34
|
static void fbuffer_clear(FBuffer *fb);
|
|
64
|
-
#endif
|
|
65
35
|
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
|
|
66
|
-
#ifdef JSON_GENERATOR
|
|
67
36
|
static void fbuffer_append_long(FBuffer *fb, long number);
|
|
68
|
-
#endif
|
|
69
37
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
|
|
70
|
-
#ifdef JSON_GENERATOR
|
|
71
38
|
static VALUE fbuffer_finalize(FBuffer *fb);
|
|
72
|
-
#endif
|
|
73
39
|
|
|
74
40
|
static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
|
|
75
41
|
{
|
|
@@ -79,6 +45,20 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
|
|
|
79
45
|
fb->ptr = stack_buffer;
|
|
80
46
|
fb->capa = stack_buffer_size;
|
|
81
47
|
}
|
|
48
|
+
#if JSON_DEBUG
|
|
49
|
+
fb->requested = 0;
|
|
50
|
+
#endif
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
|
|
54
|
+
{
|
|
55
|
+
#if JSON_DEBUG
|
|
56
|
+
if (consumed > fb->requested) {
|
|
57
|
+
rb_bug("fbuffer: Out of bound write");
|
|
58
|
+
}
|
|
59
|
+
fb->requested = 0;
|
|
60
|
+
#endif
|
|
61
|
+
fb->len += consumed;
|
|
82
62
|
}
|
|
83
63
|
|
|
84
64
|
static void fbuffer_free(FBuffer *fb)
|
|
@@ -142,73 +122,128 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
142
122
|
|
|
143
123
|
static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
|
144
124
|
{
|
|
125
|
+
#if JSON_DEBUG
|
|
126
|
+
fb->requested = requested;
|
|
127
|
+
#endif
|
|
128
|
+
|
|
145
129
|
if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
|
|
146
130
|
fbuffer_do_inc_capa(fb, requested);
|
|
147
131
|
}
|
|
148
132
|
}
|
|
149
133
|
|
|
150
|
-
static void
|
|
134
|
+
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, unsigned long len)
|
|
135
|
+
{
|
|
136
|
+
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
137
|
+
fbuffer_consumed(fb, len);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
|
|
151
141
|
{
|
|
152
142
|
if (len > 0) {
|
|
153
143
|
fbuffer_inc_capa(fb, len);
|
|
154
|
-
|
|
155
|
-
fb->len += len;
|
|
144
|
+
fbuffer_append_reserved(fb, newstr, len);
|
|
156
145
|
}
|
|
157
146
|
}
|
|
158
147
|
|
|
159
|
-
|
|
160
|
-
static void
|
|
148
|
+
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
|
149
|
+
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
161
150
|
{
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
#if JSON_DEBUG
|
|
152
|
+
if (fb->requested < 1) {
|
|
153
|
+
rb_bug("fbuffer: unreserved write");
|
|
154
|
+
}
|
|
155
|
+
fb->requested--;
|
|
156
|
+
#endif
|
|
157
|
+
|
|
158
|
+
fb->ptr[fb->len] = chr;
|
|
159
|
+
fb->len++;
|
|
160
|
+
}
|
|
164
161
|
|
|
165
|
-
|
|
162
|
+
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
|
163
|
+
{
|
|
164
|
+
const char *ptr;
|
|
165
|
+
unsigned long len;
|
|
166
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
166
167
|
|
|
167
|
-
fbuffer_append(fb,
|
|
168
|
+
fbuffer_append(fb, ptr, len);
|
|
168
169
|
}
|
|
170
|
+
|
|
171
|
+
static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
172
|
+
{
|
|
173
|
+
const char *ptr;
|
|
174
|
+
unsigned long len;
|
|
175
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
176
|
+
|
|
177
|
+
fbuffer_inc_capa(fb, repeat * len);
|
|
178
|
+
while (repeat) {
|
|
179
|
+
#if JSON_DEBUG
|
|
180
|
+
fb->requested = len;
|
|
169
181
|
#endif
|
|
182
|
+
fbuffer_append_reserved(fb, ptr, len);
|
|
183
|
+
repeat--;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
170
186
|
|
|
171
187
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
|
172
188
|
{
|
|
173
189
|
fbuffer_inc_capa(fb, 1);
|
|
174
190
|
*(fb->ptr + fb->len) = newchr;
|
|
175
|
-
fb
|
|
191
|
+
fbuffer_consumed(fb, 1);
|
|
176
192
|
}
|
|
177
193
|
|
|
178
|
-
|
|
179
|
-
static long fltoa(long number, char *buf)
|
|
194
|
+
static inline char *fbuffer_cursor(FBuffer *fb)
|
|
180
195
|
{
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
char* tmp = buf;
|
|
196
|
+
return fb->ptr + fb->len;
|
|
197
|
+
}
|
|
184
198
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return buf - tmp;
|
|
199
|
+
static inline void fbuffer_advance_to(FBuffer *fb, char *end)
|
|
200
|
+
{
|
|
201
|
+
fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
|
|
189
202
|
}
|
|
190
203
|
|
|
191
|
-
|
|
204
|
+
/*
|
|
205
|
+
* Appends the decimal string representation of \a number into the buffer.
|
|
206
|
+
*/
|
|
192
207
|
static void fbuffer_append_long(FBuffer *fb, long number)
|
|
193
208
|
{
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
209
|
+
/*
|
|
210
|
+
* The jeaiii_ultoa() function produces digits left-to-right,
|
|
211
|
+
* allowing us to write directly into the buffer, but we don't know
|
|
212
|
+
* the number of resulting characters.
|
|
213
|
+
*
|
|
214
|
+
* We do know, however, that the `number` argument is always in the
|
|
215
|
+
* range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
|
|
216
|
+
* -4611686018427387904 to 4611686018427387903. The max number of chars
|
|
217
|
+
* generated is therefore 20 (including a potential sign character).
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
static const int MAX_CHARS_FOR_LONG = 20;
|
|
221
|
+
|
|
222
|
+
fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
|
|
223
|
+
|
|
224
|
+
if (number < 0) {
|
|
225
|
+
fbuffer_append_reserved_char(fb, '-');
|
|
226
|
+
|
|
227
|
+
/*
|
|
228
|
+
* Since number is always > LONG_MIN, `-number` will not overflow
|
|
229
|
+
* and is always the positive abs() value.
|
|
230
|
+
*/
|
|
231
|
+
number = -number;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
|
|
235
|
+
fbuffer_advance_to(fb, end);
|
|
198
236
|
}
|
|
199
237
|
|
|
200
238
|
static VALUE fbuffer_finalize(FBuffer *fb)
|
|
201
239
|
{
|
|
202
240
|
if (fb->io) {
|
|
203
241
|
fbuffer_flush(fb);
|
|
204
|
-
fbuffer_free(fb);
|
|
205
242
|
rb_io_flush(fb->io);
|
|
206
243
|
return fb->io;
|
|
207
244
|
} else {
|
|
208
|
-
|
|
209
|
-
fbuffer_free(fb);
|
|
210
|
-
return result;
|
|
245
|
+
return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
|
|
211
246
|
}
|
|
212
247
|
}
|
|
213
|
-
|
|
214
|
-
#endif
|
|
248
|
+
|
|
249
|
+
#endif // _FBUFFER_H_
|
|
@@ -6,5 +6,11 @@ if RUBY_ENGINE == 'truffleruby'
|
|
|
6
6
|
else
|
|
7
7
|
append_cflags("-std=c99")
|
|
8
8
|
$defs << "-DJSON_GENERATOR"
|
|
9
|
+
$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
|
|
10
|
+
|
|
11
|
+
if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
|
|
12
|
+
load __dir__ + "/../simd/conf.rb"
|
|
13
|
+
end
|
|
14
|
+
|
|
9
15
|
create_makefile 'json/ext/generator'
|
|
10
16
|
end
|