json 2.18.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +127 -0
- data/LEGAL +3 -3
- data/README.md +19 -90
- data/ext/json/ext/fbuffer/fbuffer.h +46 -36
- data/ext/json/ext/generator/extconf.rb +3 -0
- data/ext/json/ext/generator/generator.c +200 -364
- data/ext/json/ext/json.h +95 -0
- data/ext/json/ext/parser/extconf.rb +27 -0
- data/ext/json/ext/parser/parser.c +1654 -406
- data/ext/json/ext/simd/simd.h +0 -10
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/lib/json/common.rb +184 -411
- data/lib/json/ext/generator/state.rb +2 -28
- data/lib/json/ext.rb +25 -1
- data/lib/json/truffle_ruby/generator.rb +89 -81
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +53 -264
- metadata +3 -19
- data/ext/json/ext/vendor/ryu.h +0 -819
- data/lib/json/add/bigdecimal.rb +0 -58
- data/lib/json/add/complex.rb +0 -51
- data/lib/json/add/core.rb +0 -13
- data/lib/json/add/date.rb +0 -54
- data/lib/json/add/date_time.rb +0 -67
- data/lib/json/add/exception.rb +0 -49
- data/lib/json/add/ostruct.rb +0 -54
- data/lib/json/add/range.rb +0 -54
- data/lib/json/add/rational.rb +0 -49
- data/lib/json/add/regexp.rb +0 -48
- data/lib/json/add/set.rb +0 -48
- data/lib/json/add/string.rb +0 -35
- data/lib/json/add/struct.rb +0 -52
- data/lib/json/add/symbol.rb +0 -52
- data/lib/json/add/time.rb +0 -52
- data/lib/json/generic_object.rb +0 -67
|
@@ -9,7 +9,7 @@ module JSON
|
|
|
9
9
|
# Instantiates a new State object, configured by _opts_.
|
|
10
10
|
#
|
|
11
11
|
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
|
12
|
-
# See {Generating Options}[
|
|
12
|
+
# See {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
13
13
|
def initialize(opts = nil)
|
|
14
14
|
if opts && !opts.empty?
|
|
15
15
|
configure(opts)
|
|
@@ -54,6 +54,7 @@ module JSON
|
|
|
54
54
|
strict: strict?,
|
|
55
55
|
depth: depth,
|
|
56
56
|
buffer_initial_length: buffer_initial_length,
|
|
57
|
+
sort_keys: sort_keys
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
allow_duplicate_key = allow_duplicate_key?
|
|
@@ -70,33 +71,6 @@ module JSON
|
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
alias_method :to_hash, :to_h
|
|
73
|
-
|
|
74
|
-
# call-seq: [](name)
|
|
75
|
-
#
|
|
76
|
-
# Returns the value returned by method +name+.
|
|
77
|
-
def [](name)
|
|
78
|
-
::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0")
|
|
79
|
-
|
|
80
|
-
if respond_to?(name)
|
|
81
|
-
__send__(name)
|
|
82
|
-
else
|
|
83
|
-
instance_variable_get("@#{name}") if
|
|
84
|
-
instance_variables.include?("@#{name}".to_sym) # avoid warning
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# call-seq: []=(name, value)
|
|
89
|
-
#
|
|
90
|
-
# Sets the attribute name to value.
|
|
91
|
-
def []=(name, value)
|
|
92
|
-
::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0")
|
|
93
|
-
|
|
94
|
-
if respond_to?(name_writer = "#{name}=")
|
|
95
|
-
__send__ name_writer, value
|
|
96
|
-
else
|
|
97
|
-
instance_variable_set "@#{name}", value
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
74
|
end
|
|
101
75
|
end
|
|
102
76
|
end
|
data/lib/json/ext.rb
CHANGED
|
@@ -41,5 +41,29 @@ module JSON
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
if defined?(ResumableParser) # Not yet available on JRuby
|
|
45
|
+
class ResumableParser
|
|
46
|
+
# Returns whether the parser is entirely done: no unconsumed bytes in
|
|
47
|
+
# the buffer, no document under construction and no parsed value
|
|
48
|
+
# awaiting retrieval.
|
|
49
|
+
#
|
|
50
|
+
# The main use case is detecting a truncated stream once the input is
|
|
51
|
+
# exhausted:
|
|
52
|
+
#
|
|
53
|
+
# loop do
|
|
54
|
+
# begin
|
|
55
|
+
# parser << socket.readpartial(4096)
|
|
56
|
+
# rescue EOFError
|
|
57
|
+
# break
|
|
58
|
+
# end
|
|
59
|
+
# while parser.parse
|
|
60
|
+
# process(parser.value)
|
|
61
|
+
# end
|
|
62
|
+
# end
|
|
63
|
+
# warn "stream was truncated" unless parser.empty?
|
|
64
|
+
def empty?
|
|
65
|
+
eos? && !partial_value? && !value?
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
45
69
|
end
|
|
@@ -48,7 +48,7 @@ module JSON
|
|
|
48
48
|
SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/
|
|
49
49
|
|
|
50
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)
|
|
51
|
+
(false == value || true == value || nil == value || String === value || Symbol === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def self.native_key?(key) # :nodoc:
|
|
@@ -111,6 +111,8 @@ module JSON
|
|
|
111
111
|
# This class is used to create State instances, that are use to hold data
|
|
112
112
|
# while generating a JSON text from a Ruby data structure.
|
|
113
113
|
class State
|
|
114
|
+
singleton_class.attr_accessor :default_sort_keys_proc # :nodoc:
|
|
115
|
+
|
|
114
116
|
def self.generate(obj, opts = nil, io = nil)
|
|
115
117
|
new(opts).generate(obj, io)
|
|
116
118
|
end
|
|
@@ -164,7 +166,9 @@ module JSON
|
|
|
164
166
|
@script_safe = false
|
|
165
167
|
@strict = false
|
|
166
168
|
@max_nesting = 100
|
|
167
|
-
|
|
169
|
+
@sort_keys = false
|
|
170
|
+
@allow_duplicate_key = false
|
|
171
|
+
_configure(**opts) if opts
|
|
168
172
|
end
|
|
169
173
|
|
|
170
174
|
# This string is used to indent levels in the JSON text.
|
|
@@ -199,6 +203,33 @@ module JSON
|
|
|
199
203
|
# supported by the JSON spec will raise a JSON::GeneratorError
|
|
200
204
|
attr_accessor :strict
|
|
201
205
|
|
|
206
|
+
# Controls key sorting in the generated JSON. If set to +true+, object
|
|
207
|
+
# keys are sorted by key lexicographically. If set to a Proc, it
|
|
208
|
+
# receives the entire Hash and must return a Hash with its pairs in the
|
|
209
|
+
# desired order.
|
|
210
|
+
attr_reader :sort_keys
|
|
211
|
+
|
|
212
|
+
def sort_keys=(value) # :nodoc:
|
|
213
|
+
type_error = false
|
|
214
|
+
@sort_keys = case value
|
|
215
|
+
when Proc
|
|
216
|
+
value
|
|
217
|
+
when true
|
|
218
|
+
State.default_sort_keys_proc
|
|
219
|
+
when nil, false
|
|
220
|
+
false
|
|
221
|
+
else
|
|
222
|
+
type_error = true
|
|
223
|
+
false
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
if type_error
|
|
227
|
+
raise TypeError, "The `sort_keys` argument must be a boolean or a Proc"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
@sort_keys
|
|
231
|
+
end
|
|
232
|
+
|
|
202
233
|
# :stopdoc:
|
|
203
234
|
attr_reader :buffer_initial_length
|
|
204
235
|
|
|
@@ -211,7 +242,14 @@ module JSON
|
|
|
211
242
|
|
|
212
243
|
# This integer returns the current depth data structure nesting in the
|
|
213
244
|
# generated JSON.
|
|
214
|
-
|
|
245
|
+
attr_reader :depth
|
|
246
|
+
|
|
247
|
+
def depth=(depth)
|
|
248
|
+
if depth.negative?
|
|
249
|
+
raise ArgumentError, "depth must be >= 0 (got #{depth})"
|
|
250
|
+
end
|
|
251
|
+
@depth = depth
|
|
252
|
+
end
|
|
215
253
|
|
|
216
254
|
def check_max_nesting # :nodoc:
|
|
217
255
|
return if @max_nesting.zero?
|
|
@@ -252,56 +290,48 @@ module JSON
|
|
|
252
290
|
|
|
253
291
|
# Configure this State instance with the Hash _opts_, and return
|
|
254
292
|
# itself.
|
|
255
|
-
def configure(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
end
|
|
263
|
-
opts.each do |key, value|
|
|
264
|
-
instance_variable_set "@#{key}", value
|
|
293
|
+
def configure(options)
|
|
294
|
+
unless Hash == options
|
|
295
|
+
if options.respond_to?(:to_hash)
|
|
296
|
+
options = options.to_hash
|
|
297
|
+
elsif options.respond_to?(:to_h)
|
|
298
|
+
options = options.to_h
|
|
299
|
+
end
|
|
265
300
|
end
|
|
301
|
+
_configure(**options)
|
|
302
|
+
end
|
|
303
|
+
alias merge configure
|
|
266
304
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
@as_json = opts[:as_json].to_proc if opts[:as_json]
|
|
275
|
-
@ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
|
|
276
|
-
@depth = opts[:depth] || 0
|
|
277
|
-
@buffer_initial_length ||= opts[:buffer_initial_length]
|
|
278
|
-
|
|
279
|
-
@script_safe = if opts.key?(:script_safe)
|
|
280
|
-
!!opts[:script_safe]
|
|
281
|
-
elsif opts.key?(:escape_slash)
|
|
282
|
-
!!opts[:escape_slash]
|
|
283
|
-
else
|
|
284
|
-
false
|
|
305
|
+
private def _configure(
|
|
306
|
+
indent: '', space: '', space_before: '', object_nl: '', array_nl: '', allow_nan: false,
|
|
307
|
+
as_json: false, ascii_only: false, sort_keys: false, depth: 0, buffer_initial_length: 1024,
|
|
308
|
+
allow_duplicate_key: false, script_safe: false, strict: false, max_nesting: 100
|
|
309
|
+
)
|
|
310
|
+
if depth.negative?
|
|
311
|
+
raise ArgumentError, "depth must be >= 0 (got #{depth})"
|
|
285
312
|
end
|
|
286
313
|
|
|
287
|
-
if
|
|
288
|
-
|
|
289
|
-
else
|
|
290
|
-
@allow_duplicate_key = nil # nil is deprecation
|
|
314
|
+
if max_nesting && !(Integer === max_nesting)
|
|
315
|
+
raise TypeError, ":max_nesting must be `false` or an Integer, got: #{max_nesting.class}"
|
|
291
316
|
end
|
|
292
317
|
|
|
293
|
-
@
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
318
|
+
@indent = indent || ''
|
|
319
|
+
@space = space || ''
|
|
320
|
+
@space_before = space_before || ''
|
|
321
|
+
@object_nl = object_nl || ''
|
|
322
|
+
@array_nl = array_nl || ''
|
|
323
|
+
@allow_nan = allow_nan || false
|
|
324
|
+
@as_json = as_json || false
|
|
325
|
+
@ascii_only = ascii_only || false
|
|
326
|
+
self.sort_keys = sort_keys
|
|
327
|
+
@depth = depth
|
|
328
|
+
@buffer_initial_length = buffer_initial_length
|
|
329
|
+
@allow_duplicate_key = allow_duplicate_key
|
|
330
|
+
@script_safe = script_safe
|
|
331
|
+
@strict = strict
|
|
332
|
+
@max_nesting = max_nesting || 0
|
|
302
333
|
self
|
|
303
334
|
end
|
|
304
|
-
alias merge configure
|
|
305
335
|
|
|
306
336
|
def allow_duplicate_key? # :nodoc:
|
|
307
337
|
@allow_duplicate_key
|
|
@@ -316,10 +346,6 @@ module JSON
|
|
|
316
346
|
result[key.to_sym] = instance_variable_get(iv)
|
|
317
347
|
end
|
|
318
348
|
|
|
319
|
-
if result[:allow_duplicate_key].nil?
|
|
320
|
-
result.delete(:allow_duplicate_key)
|
|
321
|
-
end
|
|
322
|
-
|
|
323
349
|
result
|
|
324
350
|
end
|
|
325
351
|
|
|
@@ -334,9 +360,13 @@ module JSON
|
|
|
334
360
|
|
|
335
361
|
depth = @depth
|
|
336
362
|
if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
|
|
337
|
-
!@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
|
|
363
|
+
!@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj) and !@sort_keys
|
|
338
364
|
result = generate_json(obj, ''.dup)
|
|
339
365
|
else
|
|
366
|
+
if @sort_keys
|
|
367
|
+
obj = @sort_keys.call(obj)
|
|
368
|
+
end
|
|
369
|
+
|
|
340
370
|
result = obj.to_json(self)
|
|
341
371
|
end
|
|
342
372
|
JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new(
|
|
@@ -366,7 +396,7 @@ module JSON
|
|
|
366
396
|
else
|
|
367
397
|
if key_type && !@allow_duplicate_key && key_type != k.class
|
|
368
398
|
key_type = nil # stop checking
|
|
369
|
-
JSON.send(:on_mixed_keys_hash, obj
|
|
399
|
+
JSON.send(:on_mixed_keys_hash, obj)
|
|
370
400
|
end
|
|
371
401
|
buf << ','
|
|
372
402
|
end
|
|
@@ -433,28 +463,6 @@ module JSON
|
|
|
433
463
|
end
|
|
434
464
|
buf << '"'
|
|
435
465
|
end
|
|
436
|
-
|
|
437
|
-
# Return the value returned by method +name+.
|
|
438
|
-
def [](name)
|
|
439
|
-
::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0")
|
|
440
|
-
|
|
441
|
-
if respond_to?(name)
|
|
442
|
-
__send__(name)
|
|
443
|
-
else
|
|
444
|
-
instance_variable_get("@#{name}") if
|
|
445
|
-
instance_variables.include?("@#{name}".to_sym) # avoid warning
|
|
446
|
-
end
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
def []=(name, value)
|
|
450
|
-
::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0")
|
|
451
|
-
|
|
452
|
-
if respond_to?(name_writer = "#{name}=")
|
|
453
|
-
__send__ name_writer, value
|
|
454
|
-
else
|
|
455
|
-
instance_variable_set "@#{name}", value
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
466
|
end
|
|
459
467
|
|
|
460
468
|
module GeneratorMethods
|
|
@@ -505,11 +513,11 @@ module JSON
|
|
|
505
513
|
|
|
506
514
|
if empty?
|
|
507
515
|
state.depth -= 1
|
|
508
|
-
return '{}'
|
|
516
|
+
return +'{}'
|
|
509
517
|
end
|
|
510
518
|
|
|
511
519
|
delim = ",#{state.object_nl}"
|
|
512
|
-
result =
|
|
520
|
+
result = "{#{state.object_nl}"
|
|
513
521
|
first = true
|
|
514
522
|
key_type = nil
|
|
515
523
|
indent = !state.object_nl.empty?
|
|
@@ -519,7 +527,7 @@ module JSON
|
|
|
519
527
|
else
|
|
520
528
|
if key_type && !state.allow_duplicate_key? && key_type != key.class
|
|
521
529
|
key_type = nil # stop checking
|
|
522
|
-
JSON.send(:on_mixed_keys_hash, self
|
|
530
|
+
JSON.send(:on_mixed_keys_hash, self)
|
|
523
531
|
end
|
|
524
532
|
result << delim
|
|
525
533
|
end
|
|
@@ -546,7 +554,7 @@ module JSON
|
|
|
546
554
|
raise TypeError, "#{key.class}#to_s returns an instance of #{key_str.class}, expected a String"
|
|
547
555
|
end
|
|
548
556
|
|
|
549
|
-
result =
|
|
557
|
+
result = "#{result}#{key_json}#{state.space_before}:#{state.space}"
|
|
550
558
|
if state.strict? && !Generator.native_type?(value)
|
|
551
559
|
if state.as_json
|
|
552
560
|
value = state.as_json.call(value, false)
|
|
@@ -597,7 +605,7 @@ module JSON
|
|
|
597
605
|
|
|
598
606
|
if empty?
|
|
599
607
|
state.depth -= 1
|
|
600
|
-
return '[]'
|
|
608
|
+
return +'[]'
|
|
601
609
|
end
|
|
602
610
|
|
|
603
611
|
result = '['.dup
|
|
@@ -722,17 +730,17 @@ module JSON
|
|
|
722
730
|
|
|
723
731
|
module TrueClass
|
|
724
732
|
# Returns a JSON string for true: 'true'.
|
|
725
|
-
def to_json(*) 'true' end
|
|
733
|
+
def to_json(*) +'true' end
|
|
726
734
|
end
|
|
727
735
|
|
|
728
736
|
module FalseClass
|
|
729
737
|
# Returns a JSON string for false: 'false'.
|
|
730
|
-
def to_json(*) 'false' end
|
|
738
|
+
def to_json(*) +'false' end
|
|
731
739
|
end
|
|
732
740
|
|
|
733
741
|
module NilClass
|
|
734
742
|
# Returns a JSON string for nil: 'null'.
|
|
735
|
-
def to_json(*) 'null' end
|
|
743
|
+
def to_json(*) +'null' end
|
|
736
744
|
end
|
|
737
745
|
end
|
|
738
746
|
end
|
data/lib/json/version.rb
CHANGED