json 3.0.1-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 +13 -0
- data/lib/json/common.rb +26 -26
- 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 +45 -37
- 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,19 @@
|
|
|
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
|
+
|
|
14
|
+
### 2026-09-09 (3.0.2)
|
|
15
|
+
|
|
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 `...`.
|
|
17
|
+
|
|
5
18
|
### 2026-09-08 (3.0.1)
|
|
6
19
|
|
|
7
20
|
* Restore the `limit` positional argument of `JSON.dump`.
|
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
|
|
@@ -143,11 +135,11 @@ module JSON
|
|
|
143
135
|
# This exception is raised if a parser error occurs.
|
|
144
136
|
class ParserError < JSONError
|
|
145
137
|
# Line number where the parser encountered an error.
|
|
146
|
-
# Is
|
|
138
|
+
# Is +nil+ when raised by JSON::ResumableParser.
|
|
147
139
|
attr_reader :line
|
|
148
140
|
|
|
149
|
-
#
|
|
150
|
-
# Is
|
|
141
|
+
# One-based column number where the parser encountered an error, counted in Unicode codepoints.
|
|
142
|
+
# Is +nil+ when raised by JSON::ResumableParser.
|
|
151
143
|
attr_reader :column
|
|
152
144
|
|
|
153
145
|
# Returns a best effort JSONPath string representing where in the document
|
|
@@ -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)
|
|
@@ -290,7 +285,7 @@ module JSON
|
|
|
290
285
|
# ---
|
|
291
286
|
#
|
|
292
287
|
# Raises an exception if +source+ is not valid JSON:
|
|
293
|
-
# # Raises JSON::ParserError unexpected character: 'invalid' at line 1 column 1
|
|
288
|
+
# # Raises JSON::ParserError (unexpected character: 'invalid' at line 1 column 1):
|
|
294
289
|
# JSON.parse('invalid')
|
|
295
290
|
#
|
|
296
291
|
def parse(source, on_load: nil, object_class: nil, array_class: nil, **options)
|
|
@@ -324,8 +319,8 @@ module JSON
|
|
|
324
319
|
# parse(File.read(path), **)
|
|
325
320
|
#
|
|
326
321
|
# See method #parse.
|
|
327
|
-
def load_file(filespec,
|
|
328
|
-
parse(File.read(filespec, encoding: Encoding::UTF_8),
|
|
322
|
+
def load_file(filespec, **options)
|
|
323
|
+
parse(File.read(filespec, encoding: Encoding::UTF_8), **options)
|
|
329
324
|
end
|
|
330
325
|
|
|
331
326
|
# :call-seq:
|
|
@@ -335,8 +330,8 @@ module JSON
|
|
|
335
330
|
# JSON.parse!(File.read(path), **)
|
|
336
331
|
#
|
|
337
332
|
# See method #parse!
|
|
338
|
-
def load_file!(filespec,
|
|
339
|
-
parse!(File.read(filespec, encoding: Encoding::UTF_8),
|
|
333
|
+
def load_file!(filespec, **options)
|
|
334
|
+
parse!(File.read(filespec, encoding: Encoding::UTF_8), **options)
|
|
340
335
|
end
|
|
341
336
|
|
|
342
337
|
# :call-seq:
|
|
@@ -372,7 +367,7 @@ module JSON
|
|
|
372
367
|
#
|
|
373
368
|
# Raises an exception if +obj+ contains circular references:
|
|
374
369
|
# a = []; b = []; a.push(b); b.push(a)
|
|
375
|
-
# # Raises JSON::NestingError (nesting of 100 is too deep):
|
|
370
|
+
# # Raises JSON::NestingError (nesting of 100 is too deep. Did you try to serialize objects with circular references?):
|
|
376
371
|
# JSON.generate(a)
|
|
377
372
|
#
|
|
378
373
|
def generate(obj, opts = nil)
|
|
@@ -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
|
|
@@ -812,9 +807,9 @@ module JSON
|
|
|
812
807
|
private_constant :EXCLUDED_GENERATOR_OPTIONS
|
|
813
808
|
|
|
814
809
|
# :call-seq:
|
|
815
|
-
# JSON.new(options
|
|
810
|
+
# JSON::Coder.new(**options, &block)
|
|
816
811
|
#
|
|
817
|
-
#
|
|
812
|
+
# Keyword arguments +options+, if given, are options for both parsing and generating.
|
|
818
813
|
# See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
|
|
819
814
|
# and {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
820
815
|
#
|
|
@@ -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
|
@@ -44,13 +44,13 @@ require 'json/common'
|
|
|
44
44
|
#
|
|
45
45
|
# You can parse a \String containing \JSON data using
|
|
46
46
|
# either of two methods:
|
|
47
|
-
# - <tt>JSON.parse(source, opts)</tt>
|
|
48
|
-
# - <tt>JSON.parse!(source, opts)</tt>
|
|
47
|
+
# - <tt>JSON.parse(source, **opts)</tt>
|
|
48
|
+
# - <tt>JSON.parse!(source, **opts)</tt>
|
|
49
49
|
#
|
|
50
50
|
# where
|
|
51
51
|
# - +source+ is a Ruby object.
|
|
52
|
-
# - +opts+
|
|
53
|
-
#
|
|
52
|
+
# - +opts+ are keyword arguments that control both input
|
|
53
|
+
# allowed and output formatting.
|
|
54
54
|
#
|
|
55
55
|
# The difference between the two methods
|
|
56
56
|
# is that JSON.parse! omits some checks
|
|
@@ -102,7 +102,7 @@ require 'json/common'
|
|
|
102
102
|
# ruby # => 1.0
|
|
103
103
|
# ruby.class # => Float
|
|
104
104
|
# ruby = JSON.parse('2.0e2')
|
|
105
|
-
# ruby # => 200
|
|
105
|
+
# ruby # => 200.0
|
|
106
106
|
# ruby.class # => Float
|
|
107
107
|
# Boolean:
|
|
108
108
|
# ruby = JSON.parse('true')
|
|
@@ -131,10 +131,10 @@ require 'json/common'
|
|
|
131
131
|
# ruby # => [0, [1, [2, [3]]]]
|
|
132
132
|
# Too deep:
|
|
133
133
|
# # Raises JSON::NestingError (nesting of 2 is too deep):
|
|
134
|
-
# JSON.parse(source,
|
|
134
|
+
# JSON.parse(source, max_nesting: 1)
|
|
135
135
|
# Bad value:
|
|
136
|
-
# # Raises TypeError (
|
|
137
|
-
# JSON.parse(source,
|
|
136
|
+
# # Raises TypeError (no implicit conversion of Symbol into Integer):
|
|
137
|
+
# JSON.parse(source, max_nesting: :foo)
|
|
138
138
|
#
|
|
139
139
|
# ---
|
|
140
140
|
#
|
|
@@ -142,11 +142,11 @@ require 'json/common'
|
|
|
142
142
|
# should be ignored or cause an error to be raised:
|
|
143
143
|
#
|
|
144
144
|
# When set to +false+, the default:
|
|
145
|
-
# JSON.parse('{"a": 1, "a":2}')
|
|
145
|
+
# JSON.parse('{"a": 1, "a": 2}') # duplicate key "a" at line 1 column 1 (JSON::ParserError)
|
|
146
146
|
#
|
|
147
147
|
# When set to +true+:
|
|
148
148
|
# # The last value is used.
|
|
149
|
-
# JSON.parse('{"a": 1, "a":2}', allow_duplicate_key: true) => {"a" => 2}
|
|
149
|
+
# JSON.parse('{"a": 1, "a": 2}', allow_duplicate_key: true) # => {"a" => 2}
|
|
150
150
|
#
|
|
151
151
|
# ---
|
|
152
152
|
#
|
|
@@ -155,15 +155,15 @@ require 'json/common'
|
|
|
155
155
|
# defaults to +false+.
|
|
156
156
|
#
|
|
157
157
|
# With the default, +false+:
|
|
158
|
-
# # Raises JSON::ParserError (
|
|
158
|
+
# # Raises JSON::ParserError (unexpected token 'NaN]' at line 1 column 2):
|
|
159
159
|
# JSON.parse('[NaN]')
|
|
160
|
-
# # Raises JSON::ParserError (
|
|
160
|
+
# # Raises JSON::ParserError (unexpected token 'Infinity]' at line 1 column 2):
|
|
161
161
|
# JSON.parse('[Infinity]')
|
|
162
|
-
# # Raises JSON::ParserError (
|
|
162
|
+
# # Raises JSON::ParserError (invalid number: '-Infinity]' at line 1 column 2):
|
|
163
163
|
# JSON.parse('[-Infinity]')
|
|
164
164
|
# Allow:
|
|
165
165
|
# source = '[NaN, Infinity, -Infinity]'
|
|
166
|
-
# ruby = JSON.parse(source,
|
|
166
|
+
# ruby = JSON.parse(source, allow_nan: true)
|
|
167
167
|
# ruby # => [NaN, Infinity, -Infinity]
|
|
168
168
|
#
|
|
169
169
|
# ---
|
|
@@ -185,10 +185,10 @@ require 'json/common'
|
|
|
185
185
|
# defaults to +false+.
|
|
186
186
|
#
|
|
187
187
|
# When set to +false+, the default:
|
|
188
|
-
# JSON.parse('/* comment */ {"a": 1, "a":2}') # unexpected
|
|
188
|
+
# JSON.parse('/* comment */ {"a": 1, "a": 2}') # unexpected token '/*' at line 1 column 1 (JSON::ParserError)
|
|
189
189
|
#
|
|
190
190
|
# When set to +true+, comments are ignored:
|
|
191
|
-
# JSON.parse('/* comment */ {"a": 1
|
|
191
|
+
# JSON.parse('/* comment */ {"a": 1} // more comment', allow_comments: true) # => {"a" => 1}
|
|
192
192
|
#
|
|
193
193
|
# ---
|
|
194
194
|
#
|
|
@@ -197,7 +197,7 @@ require 'json/common'
|
|
|
197
197
|
# defaults to +false+.
|
|
198
198
|
#
|
|
199
199
|
# With the default, +false+:
|
|
200
|
-
# JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string (JSON::ParserError)
|
|
200
|
+
# JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string: \nWorld" at line 2 column 0 (JSON::ParserError)
|
|
201
201
|
#
|
|
202
202
|
# When enabled:
|
|
203
203
|
# JSON.parse(%{"Hello\nWorld"}, allow_control_characters: true) # => "Hello\nWorld"
|
|
@@ -209,7 +209,7 @@ require 'json/common'
|
|
|
209
209
|
# defaults to +false+.
|
|
210
210
|
#
|
|
211
211
|
# With the default, +false+:
|
|
212
|
-
# JSON.parse('"Hell\o"') # invalid escape character in string (JSON::ParserError)
|
|
212
|
+
# JSON.parse('"Hell\o"') # invalid escape character in string: '\o"' at line 1 column 6 (JSON::ParserError)
|
|
213
213
|
#
|
|
214
214
|
# When enabled:
|
|
215
215
|
# JSON.parse('"Hell\o"', allow_invalid_escape: true) # => "Hello"
|
|
@@ -228,8 +228,8 @@ require 'json/common'
|
|
|
228
228
|
# ruby = JSON.parse(source)
|
|
229
229
|
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
|
230
230
|
# Use Symbols:
|
|
231
|
-
# ruby = JSON.parse(source,
|
|
232
|
-
# ruby # => {:
|
|
231
|
+
# ruby = JSON.parse(source, symbolize_names: true)
|
|
232
|
+
# ruby # => {a: "foo", b: 1.0, c: true, d: false, e: nil}
|
|
233
233
|
#
|
|
234
234
|
# ---
|
|
235
235
|
#
|
|
@@ -242,7 +242,7 @@ require 'json/common'
|
|
|
242
242
|
# ruby = JSON.parse(source)
|
|
243
243
|
# ruby.class # => Hash
|
|
244
244
|
# Use class \OpenStruct:
|
|
245
|
-
# ruby = JSON.parse(source,
|
|
245
|
+
# ruby = JSON.parse(source, object_class: OpenStruct)
|
|
246
246
|
# ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
|
|
247
247
|
#
|
|
248
248
|
# ---
|
|
@@ -256,8 +256,8 @@ require 'json/common'
|
|
|
256
256
|
# ruby = JSON.parse(source)
|
|
257
257
|
# ruby.class # => Array
|
|
258
258
|
# Use class \Set:
|
|
259
|
-
# ruby = JSON.parse(source,
|
|
260
|
-
# ruby # =>
|
|
259
|
+
# ruby = JSON.parse(source, array_class: Set)
|
|
260
|
+
# ruby # => Set["foo", 1.0, true, false, nil]
|
|
261
261
|
#
|
|
262
262
|
# === Generating \JSON
|
|
263
263
|
#
|
|
@@ -319,22 +319,22 @@ require 'json/common'
|
|
|
319
319
|
# a \String containing a \JSON string representation of the source:
|
|
320
320
|
# JSON.generate(:foo) # => '"foo"'
|
|
321
321
|
# JSON.generate(Complex(0, 0)) # => '"0+0i"'
|
|
322
|
-
# JSON.generate(Dir.new('.')) # => '"#<Dir
|
|
322
|
+
# JSON.generate(Dir.new('.')) # => '"#<Dir:0x...>"'
|
|
323
323
|
#
|
|
324
324
|
# ==== Generating Options
|
|
325
325
|
#
|
|
326
326
|
# ====== Input Options
|
|
327
327
|
#
|
|
328
328
|
# Option +allow_nan+ (boolean) specifies whether
|
|
329
|
-
# +NaN+, +Infinity+, and
|
|
329
|
+
# +NaN+, +Infinity+, and +-Infinity+ may be generated;
|
|
330
330
|
# defaults to +false+.
|
|
331
331
|
#
|
|
332
332
|
# With the default, +false+:
|
|
333
|
-
# # Raises JSON::GeneratorError (
|
|
333
|
+
# # Raises JSON::GeneratorError (NaN not allowed in JSON):
|
|
334
334
|
# JSON.generate(JSON::NaN)
|
|
335
|
-
# # Raises JSON::GeneratorError (
|
|
335
|
+
# # Raises JSON::GeneratorError (Infinity not allowed in JSON):
|
|
336
336
|
# JSON.generate(JSON::Infinity)
|
|
337
|
-
# # Raises JSON::GeneratorError (
|
|
337
|
+
# # Raises JSON::GeneratorError (-Infinity not allowed in JSON):
|
|
338
338
|
# JSON.generate(JSON::MinusInfinity)
|
|
339
339
|
#
|
|
340
340
|
# Allow:
|
|
@@ -345,14 +345,14 @@ require 'json/common'
|
|
|
345
345
|
#
|
|
346
346
|
# Option +allow_duplicate_key+ (boolean) specifies whether
|
|
347
347
|
# hashes with duplicate keys should be allowed or produce an error.
|
|
348
|
-
#
|
|
348
|
+
# Defaults to +false+, which raises an error.
|
|
349
349
|
#
|
|
350
|
-
# With the default,
|
|
351
|
-
# JSON.generate({
|
|
350
|
+
# With the default, +false+:
|
|
351
|
+
# JSON.generate({foo: 1, "foo" => 2})
|
|
352
352
|
# # detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
|
|
353
353
|
#
|
|
354
|
-
# With
|
|
355
|
-
# JSON.generate({
|
|
354
|
+
# With +true+:
|
|
355
|
+
# JSON.generate({foo: 1, "foo" => 2}, allow_duplicate_key: true)
|
|
356
356
|
# # => '{"foo":1,"foo":2}'
|
|
357
357
|
#
|
|
358
358
|
# ---
|
|
@@ -365,13 +365,13 @@ require 'json/common'
|
|
|
365
365
|
# JSON.generate(obj) # => '[[[[[[0]]]]]]'
|
|
366
366
|
#
|
|
367
367
|
# Too deep:
|
|
368
|
-
# # Raises JSON::NestingError (nesting of 2 is too deep):
|
|
368
|
+
# # Raises JSON::NestingError (nesting of 2 is too deep. Did you try to serialize objects with circular references?):
|
|
369
369
|
# JSON.generate(obj, max_nesting: 2)
|
|
370
370
|
#
|
|
371
371
|
# With +false+:
|
|
372
372
|
# obj = []
|
|
373
373
|
# obj[0] = obj
|
|
374
|
-
# # Raises
|
|
374
|
+
# # Raises SystemStackError (stack level too deep):
|
|
375
375
|
# JSON.generate(obj, max_nesting: false)
|
|
376
376
|
#
|
|
377
377
|
# Setting +max_nesting+ to +false+ or a very large number can lead to a stack overflow
|
|
@@ -410,10 +410,18 @@ require 'json/common'
|
|
|
410
410
|
# inserted before the colon in each \JSON object's pair;
|
|
411
411
|
# defaults to the empty \String, <tt>''</tt>.
|
|
412
412
|
# - Option +sort_keys+ (boolean or \Proc) controls whether and how the keys of a
|
|
413
|
-
# hash are sorted when generating the output; defaults to
|
|
413
|
+
# hash are sorted when generating the output; defaults to +false+.
|
|
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
|
|
@@ -439,7 +447,7 @@ require 'json/common'
|
|
|
439
447
|
# "foo" : [
|
|
440
448
|
# "bar",
|
|
441
449
|
# "baz"
|
|
442
|
-
#
|
|
450
|
+
# ],
|
|
443
451
|
# "bat" : {
|
|
444
452
|
# "bam" : 0,
|
|
445
453
|
# "bad" : 1
|
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
|