json 3.0.2-java → 3.1.0-java
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 +9 -0
- data/lib/json/common.rb +16 -16
- data/lib/json/ext/generator/state.rb +2 -6
- data/lib/json/ext/generator.jar +0 -0
- data/lib/json/ext/parser.jar +0 -0
- data/lib/json/ext.rb +129 -3
- data/lib/json/truffle_ruby/generator.rb +110 -30
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee1372af8bb19939f969cebf3d301b38b9d03c40e48ed62d9d0eda90eb04692f
|
|
4
|
+
data.tar.gz: b380bcb62bfd92420834b2ce75f6fc1e298be512a15f4a5ac5839e967b51c23f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e9788ac086e362d08433e0027ffc269ec4dd58075165b86b0a4fa4c2da7d9de8583d155bf79b0b4389eaab3b10ff92e3b162b808e3dd413b22b2e3473e67fdd4
|
|
7
|
+
data.tar.gz: 51800b6ce2d20e0916b1244f9d18ab10ba3727e0bb03634f723774280fab00f731b0860dd97952e4cdc81ab02fe613260a29afa6547ff2640434df116fe7f9cd
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-10-13 (3.1.0)
|
|
6
|
+
|
|
7
|
+
* Added `rfc8785` generator option to canonicalize the output. Replaces the `json-canonicalization` with numerous correctness fixes.
|
|
8
|
+
* Use the `rb_len_t` type introduced in Ruby 4.1 to allow for proper 64bit support on LP64 platforms (Windows).
|
|
9
|
+
* JRuby: parser errors now include the position (`line`, `column` and the message suffix) as well as `json_path`, matching the C extension.
|
|
10
|
+
* Improved the accuracy of parser error's `line`, `column` and snipets in presence of multibyte characters.
|
|
11
|
+
* Fix `JSON.load` to work correctly when provided a `Zlib::GzipReader` as IO source.
|
|
12
|
+
* Reject non-ASCII BINARY strings during JSON generation. This was deprecated since a long time and scheduled to be removed for 3.0.
|
|
13
|
+
|
|
5
14
|
### 2026-09-09 (3.0.2)
|
|
6
15
|
|
|
7
16
|
* Fix `JSON.load_file` and `JSON.load_file!` to load on Ruby 2.7.0 through 2.7.2, which cannot parse a leading parameter before `...`.
|
data/lib/json/common.rb
CHANGED
|
@@ -80,14 +80,6 @@ module JSON
|
|
|
80
80
|
def generator=(generator) # :nodoc:
|
|
81
81
|
old, $VERBOSE = $VERBOSE, nil
|
|
82
82
|
|
|
83
|
-
# The default proc used when the +sort_keys+ generation option is +true+.
|
|
84
|
-
# It returns a new hash with the entries sorted by their keys.
|
|
85
|
-
sort_keys_proc = ->(hash) { hash.sort.to_h }
|
|
86
|
-
if defined?(::Ractor) && Ractor.respond_to?(:shareable_lambda)
|
|
87
|
-
sort_keys_proc = Ractor.shareable_lambda(&sort_keys_proc)
|
|
88
|
-
end
|
|
89
|
-
generator::State.default_sort_keys_proc = sort_keys_proc
|
|
90
|
-
|
|
91
83
|
@generator = generator
|
|
92
84
|
if generator.const_defined?(:GeneratorMethods)
|
|
93
85
|
generator_methods = generator::GeneratorMethods
|
|
@@ -146,7 +138,7 @@ module JSON
|
|
|
146
138
|
# Is +nil+ when raised by JSON::ResumableParser.
|
|
147
139
|
attr_reader :line
|
|
148
140
|
|
|
149
|
-
#
|
|
141
|
+
# One-based column number where the parser encountered an error, counted in Unicode codepoints.
|
|
150
142
|
# Is +nil+ when raised by JSON::ResumableParser.
|
|
151
143
|
attr_reader :column
|
|
152
144
|
|
|
@@ -179,7 +171,10 @@ module JSON
|
|
|
179
171
|
when Integer
|
|
180
172
|
"[#{segment}]"
|
|
181
173
|
when String, Symbol
|
|
182
|
-
if segment.
|
|
174
|
+
if !segment.valid_encoding?
|
|
175
|
+
segment = segment.b.gsub(/["\\]/n, { '"' => '\\"', '\\' => '\\\\' }).force_encoding(Encoding::UTF_8)
|
|
176
|
+
%{["#{segment}"]}
|
|
177
|
+
elsif segment.match?(/\A[a-zA-Z\$\_][a-zA-Z\$\_0-9]*\z/)
|
|
183
178
|
".#{segment}"
|
|
184
179
|
else
|
|
185
180
|
segment = segment.to_s.gsub(/["\\]/, { '"' => '\\"', '\\' => '\\\\' })
|
|
@@ -232,7 +227,7 @@ module JSON
|
|
|
232
227
|
Fragment = Struct.new(:json) do
|
|
233
228
|
def initialize(json)
|
|
234
229
|
unless string = String.try_convert(json)
|
|
235
|
-
raise TypeError, "
|
|
230
|
+
raise TypeError, "no implicit conversion of #{json.class} into String"
|
|
236
231
|
end
|
|
237
232
|
|
|
238
233
|
super(string)
|
|
@@ -707,10 +702,10 @@ module JSON
|
|
|
707
702
|
unless source.is_a?(String)
|
|
708
703
|
if source.respond_to? :to_str
|
|
709
704
|
source = source.to_str
|
|
705
|
+
elsif source.respond_to? :read
|
|
706
|
+
source = source.read
|
|
710
707
|
elsif source.respond_to? :to_io
|
|
711
708
|
source = source.to_io.read
|
|
712
|
-
elsif source.respond_to?(:read)
|
|
713
|
-
source = source.read
|
|
714
709
|
end
|
|
715
710
|
end
|
|
716
711
|
|
|
@@ -771,7 +766,7 @@ module JSON
|
|
|
771
766
|
end
|
|
772
767
|
|
|
773
768
|
opts = {
|
|
774
|
-
allow_nan:
|
|
769
|
+
allow_nan: !(kwargs && kwargs[:rfc8785]),
|
|
775
770
|
}
|
|
776
771
|
opts[:max_nesting] = _deprecated_limit if _deprecated_limit
|
|
777
772
|
opts.merge!(kwargs) if kwargs
|
|
@@ -835,7 +830,12 @@ module JSON
|
|
|
835
830
|
#
|
|
836
831
|
# puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
|
837
832
|
#
|
|
838
|
-
|
|
833
|
+
# The conversion callback can also be passed as +as_json:+. If both are given, the block takes precedence.
|
|
834
|
+
# Passing +false+ or +nil+ for +strict:+ raises ArgumentError.
|
|
835
|
+
#
|
|
836
|
+
def initialize(object_class: nil, array_class: nil, on_load: nil, strict: true, **options, &as_json)
|
|
837
|
+
raise ArgumentError, "JSON::Coder requires strict mode" unless strict
|
|
838
|
+
|
|
839
839
|
if object_class || array_class
|
|
840
840
|
on_load = ParserOptions.on_load(on_load, object_class, array_class)
|
|
841
841
|
end
|
|
@@ -847,7 +847,7 @@ module JSON
|
|
|
847
847
|
@state = State.new(
|
|
848
848
|
**generator_options,
|
|
849
849
|
strict: true,
|
|
850
|
-
as_json: as_json,
|
|
850
|
+
as_json: as_json || generator_options[:as_json],
|
|
851
851
|
).freeze
|
|
852
852
|
end
|
|
853
853
|
|
|
@@ -54,7 +54,8 @@ module JSON
|
|
|
54
54
|
strict: strict?,
|
|
55
55
|
depth: depth,
|
|
56
56
|
buffer_initial_length: buffer_initial_length,
|
|
57
|
-
sort_keys: sort_keys
|
|
57
|
+
sort_keys: sort_keys,
|
|
58
|
+
rfc8785: rfc8785?,
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
allow_duplicate_key = allow_duplicate_key?
|
|
@@ -62,11 +63,6 @@ module JSON
|
|
|
62
63
|
result[:allow_duplicate_key] = allow_duplicate_key
|
|
63
64
|
end
|
|
64
65
|
|
|
65
|
-
instance_variables.each do |iv|
|
|
66
|
-
iv = iv.to_s[1..-1]
|
|
67
|
-
result[iv.to_sym] = self[iv]
|
|
68
|
-
end
|
|
69
|
-
|
|
70
66
|
result
|
|
71
67
|
end
|
|
72
68
|
|
data/lib/json/ext/generator.jar
CHANGED
|
Binary file
|
data/lib/json/ext/parser.jar
CHANGED
|
Binary file
|
data/lib/json/ext.rb
CHANGED
|
@@ -28,17 +28,143 @@ module JSON
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
class << self
|
|
32
|
+
if defined?(::Ractor) && Ractor.respond_to?(:shareable_lambda)
|
|
33
|
+
def shareable_lambda(block) # :nodoc:
|
|
34
|
+
Ractor.shareable_lambda(&block)
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
def shareable_lambda(block) # :nodoc:
|
|
38
|
+
block
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def valid_encoding?(str) # :nodoc:
|
|
43
|
+
str.ascii_only? || (str.encoding == Encoding::UTF_8 && str.valid_encoding?)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def coerce_key(key, as_json) # :nodoc:
|
|
47
|
+
as_json_called = false
|
|
48
|
+
|
|
49
|
+
unless String === key || Symbol === key
|
|
50
|
+
key = as_json.call(key, true)
|
|
51
|
+
as_json_called = true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if Symbol === key
|
|
55
|
+
key = key.to_s
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
unless String === key
|
|
59
|
+
raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
unless valid_encoding?(key)
|
|
63
|
+
if as_json_called
|
|
64
|
+
raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
key = as_json.call(key, true)
|
|
68
|
+
unless String === key
|
|
69
|
+
raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
unless Ext.valid_encoding?(key)
|
|
73
|
+
raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
key
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def on_duplicate_key(hash, as_json)
|
|
81
|
+
set = {}
|
|
82
|
+
hash.each_key do |key|
|
|
83
|
+
key = coerce_key(key, as_json)
|
|
84
|
+
if set[key]
|
|
85
|
+
raise GeneratorError.new("detected duplicate key #{key.inspect}", key)
|
|
86
|
+
else
|
|
87
|
+
set[key] = true
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
31
93
|
require 'json/ext/parser'
|
|
32
94
|
Ext::Parser::Config = Ext::ParserConfig
|
|
33
95
|
JSON.parser = Ext::Parser
|
|
34
96
|
|
|
35
|
-
if RUBY_ENGINE == 'truffleruby'
|
|
97
|
+
generator = if RUBY_ENGINE == 'truffleruby'
|
|
36
98
|
require 'json/truffle_ruby/generator'
|
|
37
|
-
JSON
|
|
99
|
+
JSON::TruffleRuby::Generator
|
|
38
100
|
else
|
|
39
101
|
require 'json/ext/generator'
|
|
40
|
-
|
|
102
|
+
Generator
|
|
41
103
|
end
|
|
104
|
+
|
|
105
|
+
# The default proc used when the +sort_keys+ generation option is +true+.
|
|
106
|
+
# It returns a new hash with the entries sorted by their keys.
|
|
107
|
+
generator::State.default_sort_keys_proc = shareable_lambda(->(hash) {
|
|
108
|
+
hash.sort.to_h
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
# Directly lifted from Gregg Kellogg's json-canonicalization
|
|
112
|
+
generator::State.rfc8785_number_formatter_proc = shareable_lambda(->(num) {
|
|
113
|
+
if Integer === num
|
|
114
|
+
if num > Float::MAX || num < -Float::MAX
|
|
115
|
+
raise GeneratorError.new("Integer out of range for RFC 8785", num)
|
|
116
|
+
end
|
|
117
|
+
num = num.to_f
|
|
118
|
+
end
|
|
119
|
+
if num.zero?
|
|
120
|
+
"0"
|
|
121
|
+
else
|
|
122
|
+
if num < 0
|
|
123
|
+
num, sign = -num, '-'
|
|
124
|
+
end
|
|
125
|
+
native_rep = num.to_s
|
|
126
|
+
decimal, exponential = native_rep.split('e')
|
|
127
|
+
exp_val = exponential.to_i
|
|
128
|
+
exponential = exp_val > 0 ? ('+' + exp_val.to_s) : exp_val.to_s
|
|
129
|
+
|
|
130
|
+
integral, fractional = decimal.split('.')
|
|
131
|
+
fractional = fractional.sub(/0+$/, '') # Remove trailing zeros
|
|
132
|
+
|
|
133
|
+
if exp_val > 0 && exp_val < 21
|
|
134
|
+
while exp_val > 0
|
|
135
|
+
integral += fractional.to_s[0] || '0'
|
|
136
|
+
fractional = fractional.to_s[1..-1]
|
|
137
|
+
exp_val -= 1
|
|
138
|
+
end
|
|
139
|
+
exponential = nil
|
|
140
|
+
elsif exp_val == 0
|
|
141
|
+
exponential = nil
|
|
142
|
+
elsif exp_val < 0 && exp_val > -7
|
|
143
|
+
# Small numbers are shown as 0.etc with e-6 as lower limit
|
|
144
|
+
fractional, integral, exponential = integral + fractional.to_s, '0', nil
|
|
145
|
+
fractional = ("0" * (-exp_val - 1)) + fractional
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
fractional = nil if fractional.to_s.empty?
|
|
149
|
+
sign.to_s + integral + (fractional ? ".#{fractional}" : '') + (exponential ? "e#{exponential}" : '')
|
|
150
|
+
end
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
generator::State.rfc8785_sort_keys_proc = shareable_lambda(->(hash, as_json) {
|
|
154
|
+
converted = if as_json
|
|
155
|
+
hash.transform_keys { |k| Ext.coerce_key(k, as_json) }
|
|
156
|
+
else
|
|
157
|
+
hash.transform_keys(&:to_s)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
if converted.size < hash.size
|
|
161
|
+
Ext.on_duplicate_key(hash, as_json)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
converted.sort_by { |k,| k.encode(Encoding::UTF_16) }.to_h
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
JSON.generator = generator
|
|
42
168
|
end
|
|
43
169
|
|
|
44
170
|
if defined?(ResumableParser) # Not yet available on JRuby
|
|
@@ -112,6 +112,8 @@ module JSON
|
|
|
112
112
|
# while generating a JSON text from a Ruby data structure.
|
|
113
113
|
class State
|
|
114
114
|
singleton_class.attr_accessor :default_sort_keys_proc # :nodoc:
|
|
115
|
+
singleton_class.attr_accessor :rfc8785_number_formatter_proc # :nodoc:
|
|
116
|
+
singleton_class.attr_accessor :rfc8785_sort_keys_proc # :nodoc:
|
|
115
117
|
|
|
116
118
|
def self.generate(obj, opts = nil, io = nil)
|
|
117
119
|
new(opts).generate(obj, io)
|
|
@@ -125,7 +127,7 @@ module JSON
|
|
|
125
127
|
if opts
|
|
126
128
|
case
|
|
127
129
|
when self === opts
|
|
128
|
-
return opts
|
|
130
|
+
return opts.validate_rfc8785
|
|
129
131
|
when opts.respond_to?(:to_hash)
|
|
130
132
|
return new(opts.to_hash)
|
|
131
133
|
when opts.respond_to?(:to_h)
|
|
@@ -168,6 +170,7 @@ module JSON
|
|
|
168
170
|
@max_nesting = 100
|
|
169
171
|
@sort_keys = false
|
|
170
172
|
@allow_duplicate_key = false
|
|
173
|
+
@rfc8785 = false
|
|
171
174
|
_configure(**opts) if opts
|
|
172
175
|
end
|
|
173
176
|
|
|
@@ -189,7 +192,16 @@ module JSON
|
|
|
189
192
|
attr_accessor :array_nl
|
|
190
193
|
|
|
191
194
|
# This proc converts unsupported types into native JSON types.
|
|
192
|
-
|
|
195
|
+
attr_reader :as_json
|
|
196
|
+
|
|
197
|
+
def as_json=(value)
|
|
198
|
+
raise FrozenError, "can't modify frozen #{self.class}: #{inspect}" if frozen?
|
|
199
|
+
value = value.to_proc if !(Proc === value) && value.respond_to?(:to_proc)
|
|
200
|
+
unless Proc === value
|
|
201
|
+
raise TypeError, "as_json must be a Proc"
|
|
202
|
+
end
|
|
203
|
+
@as_json = value
|
|
204
|
+
end
|
|
193
205
|
|
|
194
206
|
# This integer returns the maximum level of data structure nesting in
|
|
195
207
|
# the generated JSON, max_nesting = 0 if no maximum is checked.
|
|
@@ -209,6 +221,9 @@ module JSON
|
|
|
209
221
|
# desired order.
|
|
210
222
|
attr_reader :sort_keys
|
|
211
223
|
|
|
224
|
+
# Controls whether RFC8785 (canonicalization) mode is enabled. Otherwise returns false.
|
|
225
|
+
attr_writer :rfc8785
|
|
226
|
+
|
|
212
227
|
def sort_keys=(value) # :nodoc:
|
|
213
228
|
type_error = false
|
|
214
229
|
@sort_keys = case value
|
|
@@ -288,6 +303,27 @@ module JSON
|
|
|
288
303
|
@strict
|
|
289
304
|
end
|
|
290
305
|
|
|
306
|
+
# Returns true, if RFC8785 (canonicalization) mode is enabled. Otherwise returns false.
|
|
307
|
+
def rfc8785?
|
|
308
|
+
@rfc8785
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def validate_rfc8785 # :nodoc:
|
|
312
|
+
if @rfc8785
|
|
313
|
+
option = if !@indent.empty? then :indent
|
|
314
|
+
elsif !@space.empty? then :space
|
|
315
|
+
elsif !@space_before.empty? then :space_before
|
|
316
|
+
elsif !@object_nl.empty? then :object_nl
|
|
317
|
+
elsif !@array_nl.empty? then :array_nl
|
|
318
|
+
elsif @ascii_only then :ascii_only
|
|
319
|
+
elsif @script_safe then :script_safe
|
|
320
|
+
elsif @allow_nan then :allow_nan
|
|
321
|
+
end
|
|
322
|
+
raise ArgumentError, "#{option} cannot be used with rfc8785" if option
|
|
323
|
+
end
|
|
324
|
+
self
|
|
325
|
+
end
|
|
326
|
+
|
|
291
327
|
# Configure this State instance with the Hash _opts_, and return
|
|
292
328
|
# itself.
|
|
293
329
|
def configure(options)
|
|
@@ -302,10 +338,13 @@ module JSON
|
|
|
302
338
|
end
|
|
303
339
|
alias merge configure
|
|
304
340
|
|
|
341
|
+
# Defaults are the current values so that #configure only writes what it was given.
|
|
305
342
|
private def _configure(
|
|
306
|
-
indent:
|
|
307
|
-
|
|
308
|
-
|
|
343
|
+
indent: @indent, space: @space, space_before: @space_before, object_nl: @object_nl,
|
|
344
|
+
array_nl: @array_nl, allow_nan: @allow_nan, as_json: @as_json, ascii_only: @ascii_only,
|
|
345
|
+
sort_keys: @sort_keys, depth: @depth, buffer_initial_length: @buffer_initial_length,
|
|
346
|
+
allow_duplicate_key: @allow_duplicate_key, script_safe: @script_safe, strict: @strict,
|
|
347
|
+
max_nesting: @max_nesting, rfc8785: @rfc8785
|
|
309
348
|
)
|
|
310
349
|
if depth.negative?
|
|
311
350
|
raise ArgumentError, "depth must be >= 0 (got #{depth})"
|
|
@@ -321,7 +360,11 @@ module JSON
|
|
|
321
360
|
@object_nl = object_nl || ''
|
|
322
361
|
@array_nl = array_nl || ''
|
|
323
362
|
@allow_nan = allow_nan || false
|
|
324
|
-
|
|
363
|
+
if as_json
|
|
364
|
+
self.as_json = as_json
|
|
365
|
+
else
|
|
366
|
+
@as_json = false
|
|
367
|
+
end
|
|
325
368
|
@ascii_only = ascii_only || false
|
|
326
369
|
self.sort_keys = sort_keys
|
|
327
370
|
@depth = depth
|
|
@@ -330,7 +373,9 @@ module JSON
|
|
|
330
373
|
@script_safe = script_safe
|
|
331
374
|
@strict = strict
|
|
332
375
|
@max_nesting = max_nesting || 0
|
|
333
|
-
self
|
|
376
|
+
self.rfc8785 = rfc8785
|
|
377
|
+
|
|
378
|
+
validate_rfc8785
|
|
334
379
|
end
|
|
335
380
|
|
|
336
381
|
def allow_duplicate_key? # :nodoc:
|
|
@@ -340,13 +385,24 @@ module JSON
|
|
|
340
385
|
# Returns the configuration instance variables as a hash, that can be
|
|
341
386
|
# passed to the configure method.
|
|
342
387
|
def to_h
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
388
|
+
{
|
|
389
|
+
indent: @indent,
|
|
390
|
+
space: @space,
|
|
391
|
+
space_before: @space_before,
|
|
392
|
+
object_nl: @object_nl,
|
|
393
|
+
array_nl: @array_nl,
|
|
394
|
+
as_json: @as_json,
|
|
395
|
+
allow_nan: @allow_nan,
|
|
396
|
+
ascii_only: @ascii_only,
|
|
397
|
+
max_nesting: @max_nesting,
|
|
398
|
+
script_safe: @script_safe,
|
|
399
|
+
strict: @strict,
|
|
400
|
+
depth: @depth,
|
|
401
|
+
buffer_initial_length: @buffer_initial_length,
|
|
402
|
+
sort_keys: @sort_keys,
|
|
403
|
+
allow_duplicate_key: @allow_duplicate_key,
|
|
404
|
+
rfc8785: @rfc8785,
|
|
405
|
+
}
|
|
350
406
|
end
|
|
351
407
|
|
|
352
408
|
alias to_hash to_h
|
|
@@ -359,16 +415,14 @@ module JSON
|
|
|
359
415
|
return dup.generate(obj, anIO) if frozen?
|
|
360
416
|
|
|
361
417
|
depth = @depth
|
|
418
|
+
validate_rfc8785
|
|
362
419
|
if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
|
|
363
420
|
!@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj) and !@sort_keys
|
|
364
421
|
result = generate_json(obj, ''.dup)
|
|
365
422
|
else
|
|
366
|
-
if @sort_keys
|
|
367
|
-
obj = @sort_keys.call(obj)
|
|
368
|
-
end
|
|
369
|
-
|
|
370
423
|
result = obj.to_json(self)
|
|
371
424
|
end
|
|
425
|
+
|
|
372
426
|
JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new(
|
|
373
427
|
"source sequence #{result.inspect} is illegal/malformed utf-8",
|
|
374
428
|
obj
|
|
@@ -390,6 +444,11 @@ module JSON
|
|
|
390
444
|
buf << '{'
|
|
391
445
|
first = true
|
|
392
446
|
key_type = nil
|
|
447
|
+
if @rfc8785
|
|
448
|
+
obj = State.rfc8785_sort_keys_proc.call(obj, @strict && @as_json)
|
|
449
|
+
elsif @sort_keys
|
|
450
|
+
obj = @sort_keys.call(obj)
|
|
451
|
+
end
|
|
393
452
|
obj.each_pair do |k,v|
|
|
394
453
|
if first
|
|
395
454
|
key_type = k.class
|
|
@@ -431,7 +490,7 @@ module JSON
|
|
|
431
490
|
buf << obj.to_json(self)
|
|
432
491
|
end
|
|
433
492
|
when Integer
|
|
434
|
-
buf << obj.to_s
|
|
493
|
+
buf << (@rfc8785 ? State.rfc8785_number_formatter_proc.call(obj) : obj.to_s)
|
|
435
494
|
when Symbol
|
|
436
495
|
if @strict
|
|
437
496
|
fast_serialize_string(obj.name, buf)
|
|
@@ -500,10 +559,12 @@ module JSON
|
|
|
500
559
|
def to_json(state = nil, *)
|
|
501
560
|
state = State.from_state(state)
|
|
502
561
|
depth = state.depth
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
562
|
+
begin
|
|
563
|
+
state.check_max_nesting
|
|
564
|
+
json_transform(state)
|
|
565
|
+
ensure
|
|
566
|
+
state.depth = depth
|
|
567
|
+
end
|
|
507
568
|
end
|
|
508
569
|
|
|
509
570
|
private
|
|
@@ -521,13 +582,21 @@ module JSON
|
|
|
521
582
|
first = true
|
|
522
583
|
key_type = nil
|
|
523
584
|
indent = !state.object_nl.empty?
|
|
524
|
-
|
|
585
|
+
hash = self
|
|
586
|
+
|
|
587
|
+
if state.rfc8785?
|
|
588
|
+
hash = State.rfc8785_sort_keys_proc.call(hash, state.strict? && state.as_json)
|
|
589
|
+
elsif state.sort_keys
|
|
590
|
+
hash = state.sort_keys.call(hash)
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
hash.each { |key, value|
|
|
525
594
|
if first
|
|
526
595
|
key_type = key.class
|
|
527
596
|
else
|
|
528
597
|
if key_type && !state.allow_duplicate_key? && key_type != key.class
|
|
529
598
|
key_type = nil # stop checking
|
|
530
|
-
JSON.send(:on_mixed_keys_hash,
|
|
599
|
+
JSON.send(:on_mixed_keys_hash, hash)
|
|
531
600
|
end
|
|
532
601
|
result << delim
|
|
533
602
|
end
|
|
@@ -592,10 +661,12 @@ module JSON
|
|
|
592
661
|
def to_json(state = nil, *)
|
|
593
662
|
state = State.from_state(state)
|
|
594
663
|
depth = state.depth
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
664
|
+
begin
|
|
665
|
+
state.check_max_nesting
|
|
666
|
+
json_transform(state)
|
|
667
|
+
ensure
|
|
668
|
+
state.depth = depth
|
|
669
|
+
end
|
|
599
670
|
end
|
|
600
671
|
|
|
601
672
|
private
|
|
@@ -648,13 +719,20 @@ module JSON
|
|
|
648
719
|
|
|
649
720
|
module Integer
|
|
650
721
|
# Returns a JSON string representation for this Integer number.
|
|
651
|
-
def to_json(*)
|
|
722
|
+
def to_json(state = nil, *)
|
|
723
|
+
if state && State.from_state(state).rfc8785?
|
|
724
|
+
State.rfc8785_number_formatter_proc.call(self)
|
|
725
|
+
else
|
|
726
|
+
to_s
|
|
727
|
+
end
|
|
728
|
+
end
|
|
652
729
|
end
|
|
653
730
|
|
|
654
731
|
module Float
|
|
655
732
|
# Returns a JSON string representation for this Float number.
|
|
656
733
|
def to_json(state = nil, *args)
|
|
657
734
|
state = State.from_state(state)
|
|
735
|
+
|
|
658
736
|
if infinite? || nan?
|
|
659
737
|
if state.allow_nan?
|
|
660
738
|
to_s
|
|
@@ -676,6 +754,8 @@ module JSON
|
|
|
676
754
|
else
|
|
677
755
|
raise GeneratorError.new("#{self} not allowed in JSON", self)
|
|
678
756
|
end
|
|
757
|
+
elsif state.rfc8785?
|
|
758
|
+
State.rfc8785_number_formatter_proc.call(self)
|
|
679
759
|
else
|
|
680
760
|
to_s
|
|
681
761
|
end
|
data/lib/json/version.rb
CHANGED
data/lib/json.rb
CHANGED
|
@@ -414,6 +414,14 @@ require 'json/common'
|
|
|
414
414
|
# When +true+, keys are sorted lexicographically. When a \Proc, it receives
|
|
415
415
|
# the entire \Hash and must return a \Hash with its pairs in the desired
|
|
416
416
|
# order, allowing for arbitrary sort orders.
|
|
417
|
+
# - Option +rfc8785+ (boolean) controls whether the generated JSON will be canonicalized
|
|
418
|
+
# as defined in RFC8785.
|
|
419
|
+
# Integers are converted to Float before formatting and may be rounded.
|
|
420
|
+
# Integers outside the finite Float range raise JSON::GeneratorError.
|
|
421
|
+
# Nonempty +indent+, +space+, +space_before+, +object_nl+, or +array_nl+,
|
|
422
|
+
# and enabled +ascii_only+, +script_safe+, or +allow_nan+ raise ArgumentError.
|
|
423
|
+
# JSON.dump defaults +allow_nan+ to +false+ when +rfc8785+ is enabled.
|
|
424
|
+
# JSON::Fragment contents are inserted as is; the caller must ensure they are canonical.
|
|
417
425
|
#
|
|
418
426
|
# In this example, +obj+ is used first to generate the shortest
|
|
419
427
|
# \JSON data (no whitespace), then again with all formatting options
|
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: 3.0
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Luz
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-13 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: A JSON implementation as a JRuby extension.
|
|
13
13
|
email: dev+ruby@mernen.com
|