ed-precompiled_json 2.15.1-arm64-darwin

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/BSDL +22 -0
  3. data/CHANGES.md +693 -0
  4. data/COPYING +56 -0
  5. data/LEGAL +8 -0
  6. data/README.md +283 -0
  7. data/ext/json/ext/fbuffer/fbuffer.h +296 -0
  8. data/ext/json/ext/generator/extconf.rb +16 -0
  9. data/ext/json/ext/generator/generator.c +2169 -0
  10. data/ext/json/ext/parser/extconf.rb +15 -0
  11. data/ext/json/ext/parser/parser.c +1557 -0
  12. data/ext/json/ext/simd/conf.rb +24 -0
  13. data/ext/json/ext/simd/simd.h +188 -0
  14. data/ext/json/ext/vendor/fpconv.c +480 -0
  15. data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
  16. data/json.gemspec +62 -0
  17. data/lib/json/add/bigdecimal.rb +58 -0
  18. data/lib/json/add/complex.rb +51 -0
  19. data/lib/json/add/core.rb +13 -0
  20. data/lib/json/add/date.rb +54 -0
  21. data/lib/json/add/date_time.rb +67 -0
  22. data/lib/json/add/exception.rb +49 -0
  23. data/lib/json/add/ostruct.rb +54 -0
  24. data/lib/json/add/range.rb +54 -0
  25. data/lib/json/add/rational.rb +49 -0
  26. data/lib/json/add/regexp.rb +48 -0
  27. data/lib/json/add/set.rb +48 -0
  28. data/lib/json/add/string.rb +35 -0
  29. data/lib/json/add/struct.rb +52 -0
  30. data/lib/json/add/symbol.rb +52 -0
  31. data/lib/json/add/time.rb +52 -0
  32. data/lib/json/common.rb +1130 -0
  33. data/lib/json/ext/3.0/generator.bundle +0 -0
  34. data/lib/json/ext/3.0/parser.bundle +0 -0
  35. data/lib/json/ext/3.1/generator.bundle +0 -0
  36. data/lib/json/ext/3.1/parser.bundle +0 -0
  37. data/lib/json/ext/3.2/generator.bundle +0 -0
  38. data/lib/json/ext/3.2/parser.bundle +0 -0
  39. data/lib/json/ext/3.3/generator.bundle +0 -0
  40. data/lib/json/ext/3.3/parser.bundle +0 -0
  41. data/lib/json/ext/3.4/generator.bundle +0 -0
  42. data/lib/json/ext/3.4/parser.bundle +0 -0
  43. data/lib/json/ext/generator/state.rb +99 -0
  44. data/lib/json/ext.rb +57 -0
  45. data/lib/json/generic_object.rb +67 -0
  46. data/lib/json/truffle_ruby/generator.rb +708 -0
  47. data/lib/json/version.rb +5 -0
  48. data/lib/json.rb +642 -0
  49. metadata +99 -0
@@ -0,0 +1,1130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json/version'
4
+
5
+ module JSON
6
+ autoload :GenericObject, 'json/generic_object'
7
+
8
+ module ParserOptions # :nodoc:
9
+ class << self
10
+ def prepare(opts)
11
+ if opts[:object_class] || opts[:array_class]
12
+ opts = opts.dup
13
+ on_load = opts[:on_load]
14
+
15
+ on_load = object_class_proc(opts[:object_class], on_load) if opts[:object_class]
16
+ on_load = array_class_proc(opts[:array_class], on_load) if opts[:array_class]
17
+ opts[:on_load] = on_load
18
+ end
19
+
20
+ if opts.fetch(:create_additions, false) != false
21
+ opts = create_additions_proc(opts)
22
+ end
23
+
24
+ opts
25
+ end
26
+
27
+ private
28
+
29
+ def object_class_proc(object_class, on_load)
30
+ ->(obj) do
31
+ if Hash === obj
32
+ object = object_class.new
33
+ obj.each { |k, v| object[k] = v }
34
+ obj = object
35
+ end
36
+ on_load.nil? ? obj : on_load.call(obj)
37
+ end
38
+ end
39
+
40
+ def array_class_proc(array_class, on_load)
41
+ ->(obj) do
42
+ if Array === obj
43
+ array = array_class.new
44
+ obj.each { |v| array << v }
45
+ obj = array
46
+ end
47
+ on_load.nil? ? obj : on_load.call(obj)
48
+ end
49
+ end
50
+
51
+ # TODO: extract :create_additions support to another gem for version 3.0
52
+ def create_additions_proc(opts)
53
+ if opts[:symbolize_names]
54
+ raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction"
55
+ end
56
+
57
+ opts = opts.dup
58
+ create_additions = opts.fetch(:create_additions, false)
59
+ on_load = opts[:on_load]
60
+ object_class = opts[:object_class] || Hash
61
+
62
+ opts[:on_load] = ->(object) do
63
+ case object
64
+ when String
65
+ opts[:match_string]&.each do |pattern, klass|
66
+ if match = pattern.match(object)
67
+ create_additions_warning if create_additions.nil?
68
+ object = klass.json_create(object)
69
+ break
70
+ end
71
+ end
72
+ when object_class
73
+ if opts[:create_additions] != false
74
+ if class_name = object[JSON.create_id]
75
+ klass = JSON.deep_const_get(class_name)
76
+ if klass.respond_to?(:json_creatable?) ? klass.json_creatable? : klass.respond_to?(:json_create)
77
+ create_additions_warning if create_additions.nil?
78
+ object = klass.json_create(object)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ on_load.nil? ? object : on_load.call(object)
85
+ end
86
+
87
+ opts
88
+ end
89
+
90
+ def create_additions_warning
91
+ JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \
92
+ "and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
93
+ "pass `create_additions: true`"
94
+ end
95
+ end
96
+ end
97
+
98
+ class << self
99
+ def deprecation_warning(message, uplevel = 3) # :nodoc:
100
+ gem_root = File.expand_path("..", __dir__) + "/"
101
+ caller_locations(uplevel, 10).each do |frame|
102
+ if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
103
+ uplevel += 1
104
+ else
105
+ break
106
+ end
107
+ end
108
+
109
+ if RUBY_VERSION >= "3.0"
110
+ warn(message, uplevel: uplevel, category: :deprecated)
111
+ else
112
+ warn(message, uplevel: uplevel)
113
+ end
114
+ end
115
+
116
+ # :call-seq:
117
+ # JSON[object] -> new_array or new_string
118
+ #
119
+ # If +object+ is a \String,
120
+ # calls JSON.parse with +object+ and +opts+ (see method #parse):
121
+ # json = '[0, 1, null]'
122
+ # JSON[json]# => [0, 1, nil]
123
+ #
124
+ # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
125
+ # ruby = [0, 1, nil]
126
+ # JSON[ruby] # => '[0,1,null]'
127
+ def [](object, opts = nil)
128
+ if object.is_a?(String)
129
+ return JSON.parse(object, opts)
130
+ elsif object.respond_to?(:to_str)
131
+ str = object.to_str
132
+ if str.is_a?(String)
133
+ return JSON.parse(str, opts)
134
+ end
135
+ end
136
+
137
+ JSON.generate(object, opts)
138
+ end
139
+
140
+ # Returns the JSON parser class that is used by JSON.
141
+ attr_reader :parser
142
+
143
+ # Set the JSON parser class _parser_ to be used by JSON.
144
+ def parser=(parser) # :nodoc:
145
+ @parser = parser
146
+ remove_const :Parser if const_defined?(:Parser, false)
147
+ const_set :Parser, parser
148
+ end
149
+
150
+ # Return the constant located at _path_. The format of _path_ has to be
151
+ # either ::A::B::C or A::B::C. In any case, A has to be located at the top
152
+ # level (absolute namespace path?). If there doesn't exist a constant at
153
+ # the given path, an ArgumentError is raised.
154
+ def deep_const_get(path) # :nodoc:
155
+ Object.const_get(path)
156
+ rescue NameError => e
157
+ raise ArgumentError, "can't get const #{path}: #{e}"
158
+ end
159
+
160
+ # Set the module _generator_ to be used by JSON.
161
+ def generator=(generator) # :nodoc:
162
+ old, $VERBOSE = $VERBOSE, nil
163
+ @generator = generator
164
+ generator_methods = generator::GeneratorMethods
165
+ for const in generator_methods.constants
166
+ klass = const_get(const)
167
+ modul = generator_methods.const_get(const)
168
+ klass.class_eval do
169
+ instance_methods(false).each do |m|
170
+ m.to_s == 'to_json' and remove_method m
171
+ end
172
+ include modul
173
+ end
174
+ end
175
+ self.state = generator::State
176
+ const_set :State, state
177
+ ensure
178
+ $VERBOSE = old
179
+ end
180
+
181
+ # Returns the JSON generator module that is used by JSON.
182
+ attr_reader :generator
183
+
184
+ # Sets or Returns the JSON generator state class that is used by JSON.
185
+ attr_accessor :state
186
+
187
+ private
188
+
189
+ # Called from the extension when a hash has both string and symbol keys
190
+ def on_mixed_keys_hash(hash, do_raise)
191
+ set = {}
192
+ hash.each_key do |key|
193
+ key_str = key.to_s
194
+
195
+ if set[key_str]
196
+ message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
197
+ if do_raise
198
+ raise GeneratorError, message
199
+ else
200
+ deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`")
201
+ end
202
+ else
203
+ set[key_str] = true
204
+ end
205
+ end
206
+ end
207
+
208
+ def deprecated_singleton_attr_accessor(*attrs)
209
+ args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
210
+ attrs.each do |attr|
211
+ singleton_class.class_eval <<~RUBY
212
+ def #{attr}
213
+ warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
214
+ @#{attr}
215
+ end
216
+
217
+ def #{attr}=(val)
218
+ warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
219
+ @#{attr} = val
220
+ end
221
+
222
+ def _#{attr}
223
+ @#{attr}
224
+ end
225
+ RUBY
226
+ end
227
+ end
228
+ end
229
+
230
+ # Sets create identifier, which is used to decide if the _json_create_
231
+ # hook of a class should be called; initial value is +json_class+:
232
+ # JSON.create_id # => 'json_class'
233
+ def self.create_id=(new_value)
234
+ Thread.current[:"JSON.create_id"] = new_value.dup.freeze
235
+ end
236
+
237
+ # Returns the current create identifier.
238
+ # See also JSON.create_id=.
239
+ def self.create_id
240
+ Thread.current[:"JSON.create_id"] || 'json_class'
241
+ end
242
+
243
+ NaN = Float::NAN
244
+
245
+ Infinity = Float::INFINITY
246
+
247
+ MinusInfinity = -Infinity
248
+
249
+ # The base exception for JSON errors.
250
+ class JSONError < StandardError; end
251
+
252
+ # This exception is raised if a parser error occurs.
253
+ class ParserError < JSONError
254
+ attr_reader :line, :column
255
+ end
256
+
257
+ # This exception is raised if the nesting of parsed data structures is too
258
+ # deep.
259
+ class NestingError < ParserError; end
260
+
261
+ # This exception is raised if a generator or unparser error occurs.
262
+ class GeneratorError < JSONError
263
+ attr_reader :invalid_object
264
+
265
+ def initialize(message, invalid_object = nil)
266
+ super(message)
267
+ @invalid_object = invalid_object
268
+ end
269
+
270
+ def detailed_message(...)
271
+ # Exception#detailed_message doesn't exist until Ruby 3.2
272
+ super_message = defined?(super) ? super : message
273
+
274
+ if @invalid_object.nil?
275
+ super_message
276
+ else
277
+ "#{super_message}\nInvalid object: #{@invalid_object.inspect}"
278
+ end
279
+ end
280
+ end
281
+
282
+ # Fragment of JSON document that is to be included as is:
283
+ # fragment = JSON::Fragment.new("[1, 2, 3]")
284
+ # JSON.generate({ count: 3, items: fragments })
285
+ #
286
+ # This allows to easily assemble multiple JSON fragments that have
287
+ # been persisted somewhere without having to parse them nor resorting
288
+ # to string interpolation.
289
+ #
290
+ # Note: no validation is performed on the provided string. It is the
291
+ # responsibility of the caller to ensure the string contains valid JSON.
292
+ Fragment = Struct.new(:json) do
293
+ def initialize(json)
294
+ unless string = String.try_convert(json)
295
+ raise TypeError, " no implicit conversion of #{json.class} into String"
296
+ end
297
+
298
+ super(string)
299
+ end
300
+
301
+ def to_json(state = nil, *)
302
+ json
303
+ end
304
+ end
305
+
306
+ module_function
307
+
308
+ # :call-seq:
309
+ # JSON.parse(source, opts) -> object
310
+ #
311
+ # Returns the Ruby objects created by parsing the given +source+.
312
+ #
313
+ # Argument +source+ contains the \String to be parsed.
314
+ #
315
+ # Argument +opts+, if given, contains a \Hash of options for the parsing.
316
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
317
+ #
318
+ # ---
319
+ #
320
+ # When +source+ is a \JSON array, returns a Ruby \Array:
321
+ # source = '["foo", 1.0, true, false, null]'
322
+ # ruby = JSON.parse(source)
323
+ # ruby # => ["foo", 1.0, true, false, nil]
324
+ # ruby.class # => Array
325
+ #
326
+ # When +source+ is a \JSON object, returns a Ruby \Hash:
327
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
328
+ # ruby = JSON.parse(source)
329
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
330
+ # ruby.class # => Hash
331
+ #
332
+ # For examples of parsing for all \JSON data types, see
333
+ # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
334
+ #
335
+ # Parses nested JSON objects:
336
+ # source = <<~JSON
337
+ # {
338
+ # "name": "Dave",
339
+ # "age" :40,
340
+ # "hats": [
341
+ # "Cattleman's",
342
+ # "Panama",
343
+ # "Tophat"
344
+ # ]
345
+ # }
346
+ # JSON
347
+ # ruby = JSON.parse(source)
348
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
349
+ #
350
+ # ---
351
+ #
352
+ # Raises an exception if +source+ is not valid JSON:
353
+ # # Raises JSON::ParserError (783: unexpected token at ''):
354
+ # JSON.parse('')
355
+ #
356
+ def parse(source, opts = nil)
357
+ opts = ParserOptions.prepare(opts) unless opts.nil?
358
+ Parser.parse(source, opts)
359
+ end
360
+
361
+ PARSE_L_OPTIONS = {
362
+ max_nesting: false,
363
+ allow_nan: true,
364
+ }.freeze
365
+ private_constant :PARSE_L_OPTIONS
366
+
367
+ # :call-seq:
368
+ # JSON.parse!(source, opts) -> object
369
+ #
370
+ # Calls
371
+ # parse(source, opts)
372
+ # with +source+ and possibly modified +opts+.
373
+ #
374
+ # Differences from JSON.parse:
375
+ # - Option +max_nesting+, if not provided, defaults to +false+,
376
+ # which disables checking for nesting depth.
377
+ # - Option +allow_nan+, if not provided, defaults to +true+.
378
+ def parse!(source, opts = nil)
379
+ if opts.nil?
380
+ parse(source, PARSE_L_OPTIONS)
381
+ else
382
+ parse(source, PARSE_L_OPTIONS.merge(opts))
383
+ end
384
+ end
385
+
386
+ # :call-seq:
387
+ # JSON.load_file(path, opts={}) -> object
388
+ #
389
+ # Calls:
390
+ # parse(File.read(path), opts)
391
+ #
392
+ # See method #parse.
393
+ def load_file(filespec, opts = nil)
394
+ parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
395
+ end
396
+
397
+ # :call-seq:
398
+ # JSON.load_file!(path, opts = {})
399
+ #
400
+ # Calls:
401
+ # JSON.parse!(File.read(path, opts))
402
+ #
403
+ # See method #parse!
404
+ def load_file!(filespec, opts = nil)
405
+ parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
406
+ end
407
+
408
+ # :call-seq:
409
+ # JSON.generate(obj, opts = nil) -> new_string
410
+ #
411
+ # Returns a \String containing the generated \JSON data.
412
+ #
413
+ # See also JSON.pretty_generate.
414
+ #
415
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
416
+ #
417
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
418
+ # See {Generating Options}[#module-JSON-label-Generating+Options].
419
+ #
420
+ # ---
421
+ #
422
+ # When +obj+ is an \Array, returns a \String containing a \JSON array:
423
+ # obj = ["foo", 1.0, true, false, nil]
424
+ # json = JSON.generate(obj)
425
+ # json # => '["foo",1.0,true,false,null]'
426
+ #
427
+ # When +obj+ is a \Hash, returns a \String containing a \JSON object:
428
+ # obj = {foo: 0, bar: 's', baz: :bat}
429
+ # json = JSON.generate(obj)
430
+ # json # => '{"foo":0,"bar":"s","baz":"bat"}'
431
+ #
432
+ # For examples of generating from other Ruby objects, see
433
+ # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
434
+ #
435
+ # ---
436
+ #
437
+ # Raises an exception if any formatting option is not a \String.
438
+ #
439
+ # Raises an exception if +obj+ contains circular references:
440
+ # a = []; b = []; a.push(b); b.push(a)
441
+ # # Raises JSON::NestingError (nesting of 100 is too deep):
442
+ # JSON.generate(a)
443
+ #
444
+ def generate(obj, opts = nil)
445
+ if State === opts
446
+ opts.generate(obj)
447
+ else
448
+ State.generate(obj, opts, nil)
449
+ end
450
+ end
451
+
452
+ # :call-seq:
453
+ # JSON.fast_generate(obj, opts) -> new_string
454
+ #
455
+ # Arguments +obj+ and +opts+ here are the same as
456
+ # arguments +obj+ and +opts+ in JSON.generate.
457
+ #
458
+ # By default, generates \JSON data without checking
459
+ # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
460
+ #
461
+ # Raises an exception if +obj+ contains circular references:
462
+ # a = []; b = []; a.push(b); b.push(a)
463
+ # # Raises SystemStackError (stack level too deep):
464
+ # JSON.fast_generate(a)
465
+ def fast_generate(obj, opts = nil)
466
+ if RUBY_VERSION >= "3.0"
467
+ warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
468
+ else
469
+ warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
470
+ end
471
+ generate(obj, opts)
472
+ end
473
+
474
+ PRETTY_GENERATE_OPTIONS = {
475
+ indent: ' ',
476
+ space: ' ',
477
+ object_nl: "\n",
478
+ array_nl: "\n",
479
+ }.freeze
480
+ private_constant :PRETTY_GENERATE_OPTIONS
481
+
482
+ # :call-seq:
483
+ # JSON.pretty_generate(obj, opts = nil) -> new_string
484
+ #
485
+ # Arguments +obj+ and +opts+ here are the same as
486
+ # arguments +obj+ and +opts+ in JSON.generate.
487
+ #
488
+ # Default options are:
489
+ # {
490
+ # indent: ' ', # Two spaces
491
+ # space: ' ', # One space
492
+ # array_nl: "\n", # Newline
493
+ # object_nl: "\n" # Newline
494
+ # }
495
+ #
496
+ # Example:
497
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
498
+ # json = JSON.pretty_generate(obj)
499
+ # puts json
500
+ # Output:
501
+ # {
502
+ # "foo": [
503
+ # "bar",
504
+ # "baz"
505
+ # ],
506
+ # "bat": {
507
+ # "bam": 0,
508
+ # "bad": 1
509
+ # }
510
+ # }
511
+ #
512
+ def pretty_generate(obj, opts = nil)
513
+ return opts.generate(obj) if State === opts
514
+
515
+ options = PRETTY_GENERATE_OPTIONS
516
+
517
+ if opts
518
+ unless opts.is_a?(Hash)
519
+ if opts.respond_to? :to_hash
520
+ opts = opts.to_hash
521
+ elsif opts.respond_to? :to_h
522
+ opts = opts.to_h
523
+ else
524
+ raise TypeError, "can't convert #{opts.class} into Hash"
525
+ end
526
+ end
527
+ options = options.merge(opts)
528
+ end
529
+
530
+ State.generate(obj, options, nil)
531
+ end
532
+
533
+ # Sets or returns default options for the JSON.unsafe_load method.
534
+ # Initially:
535
+ # opts = JSON.load_default_options
536
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
537
+ deprecated_singleton_attr_accessor :unsafe_load_default_options
538
+
539
+ @unsafe_load_default_options = {
540
+ :max_nesting => false,
541
+ :allow_nan => true,
542
+ :allow_blank => true,
543
+ :create_additions => true,
544
+ }
545
+
546
+ # Sets or returns default options for the JSON.load method.
547
+ # Initially:
548
+ # opts = JSON.load_default_options
549
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
550
+ deprecated_singleton_attr_accessor :load_default_options
551
+
552
+ @load_default_options = {
553
+ :allow_nan => true,
554
+ :allow_blank => true,
555
+ :create_additions => nil,
556
+ }
557
+ # :call-seq:
558
+ # JSON.unsafe_load(source, proc = nil, options = {}) -> object
559
+ #
560
+ # Returns the Ruby objects created by parsing the given +source+.
561
+ #
562
+ # BEWARE: This method is meant to serialise data from trusted user input,
563
+ # like from your own database server or clients under your control, it could
564
+ # be dangerous to allow untrusted users to pass JSON sources into it.
565
+ #
566
+ # - Argument +source+ must be, or be convertible to, a \String:
567
+ # - If +source+ responds to instance method +to_str+,
568
+ # <tt>source.to_str</tt> becomes the source.
569
+ # - If +source+ responds to instance method +to_io+,
570
+ # <tt>source.to_io.read</tt> becomes the source.
571
+ # - If +source+ responds to instance method +read+,
572
+ # <tt>source.read</tt> becomes the source.
573
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
574
+ # - Option +allow_blank+ specifies a truthy value.
575
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
576
+ # - Otherwise, +source+ remains the source.
577
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
578
+ # It will be called recursively with each result (depth-first order).
579
+ # See details below.
580
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
581
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
582
+ # The default options can be changed via method JSON.unsafe_load_default_options=.
583
+ #
584
+ # ---
585
+ #
586
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
587
+ # <tt>parse(source, opts)</tt>; see #parse.
588
+ #
589
+ # Source for following examples:
590
+ # source = <<~JSON
591
+ # {
592
+ # "name": "Dave",
593
+ # "age" :40,
594
+ # "hats": [
595
+ # "Cattleman's",
596
+ # "Panama",
597
+ # "Tophat"
598
+ # ]
599
+ # }
600
+ # JSON
601
+ #
602
+ # Load a \String:
603
+ # ruby = JSON.unsafe_load(source)
604
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
605
+ #
606
+ # Load an \IO object:
607
+ # require 'stringio'
608
+ # object = JSON.unsafe_load(StringIO.new(source))
609
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
610
+ #
611
+ # Load a \File object:
612
+ # path = 't.json'
613
+ # File.write(path, source)
614
+ # File.open(path) do |file|
615
+ # JSON.unsafe_load(file)
616
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
617
+ #
618
+ # ---
619
+ #
620
+ # When +proc+ is given:
621
+ # - Modifies +source+ as above.
622
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
623
+ # - Recursively calls <tt>proc(result)</tt>.
624
+ # - Returns the final result.
625
+ #
626
+ # Example:
627
+ # require 'json'
628
+ #
629
+ # # Some classes for the example.
630
+ # class Base
631
+ # def initialize(attributes)
632
+ # @attributes = attributes
633
+ # end
634
+ # end
635
+ # class User < Base; end
636
+ # class Account < Base; end
637
+ # class Admin < Base; end
638
+ # # The JSON source.
639
+ # json = <<-EOF
640
+ # {
641
+ # "users": [
642
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
643
+ # {"type": "User", "username": "john", "email": "john@example.com"}
644
+ # ],
645
+ # "accounts": [
646
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
647
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
648
+ # ],
649
+ # "admins": {"type": "Admin", "password": "0wn3d"}
650
+ # }
651
+ # EOF
652
+ # # Deserializer method.
653
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
654
+ # type = obj.is_a?(Hash) && obj["type"]
655
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
656
+ # end
657
+ # # Call to JSON.unsafe_load
658
+ # ruby = JSON.unsafe_load(json, proc {|obj|
659
+ # case obj
660
+ # when Hash
661
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
662
+ # when Array
663
+ # obj.map! {|v| deserialize_obj v }
664
+ # end
665
+ # obj
666
+ # })
667
+ # pp ruby
668
+ # Output:
669
+ # {"users"=>
670
+ # [#<User:0x00000000064c4c98
671
+ # @attributes=
672
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
673
+ # #<User:0x00000000064c4bd0
674
+ # @attributes=
675
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
676
+ # "accounts"=>
677
+ # [{"account"=>
678
+ # #<Account:0x00000000064c4928
679
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
680
+ # {"account"=>
681
+ # #<Account:0x00000000064c4680
682
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
683
+ # "admins"=>
684
+ # #<Admin:0x00000000064c41f8
685
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
686
+ #
687
+ def unsafe_load(source, proc = nil, options = nil)
688
+ opts = if options.nil?
689
+ _unsafe_load_default_options
690
+ else
691
+ _unsafe_load_default_options.merge(options)
692
+ end
693
+
694
+ unless source.is_a?(String)
695
+ if source.respond_to? :to_str
696
+ source = source.to_str
697
+ elsif source.respond_to? :to_io
698
+ source = source.to_io.read
699
+ elsif source.respond_to?(:read)
700
+ source = source.read
701
+ end
702
+ end
703
+
704
+ if opts[:allow_blank] && (source.nil? || source.empty?)
705
+ source = 'null'
706
+ end
707
+
708
+ if proc
709
+ opts = opts.dup
710
+ opts[:on_load] = proc.to_proc
711
+ end
712
+
713
+ parse(source, opts)
714
+ end
715
+
716
+ # :call-seq:
717
+ # JSON.load(source, proc = nil, options = {}) -> object
718
+ #
719
+ # Returns the Ruby objects created by parsing the given +source+.
720
+ #
721
+ # BEWARE: This method is meant to serialise data from trusted user input,
722
+ # like from your own database server or clients under your control, it could
723
+ # be dangerous to allow untrusted users to pass JSON sources into it.
724
+ # If you must use it, use JSON.unsafe_load instead to make it clear.
725
+ #
726
+ # Since JSON version 2.8.0, `load` emits a deprecation warning when a
727
+ # non native type is deserialized, without `create_additions` being explicitly
728
+ # enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
729
+ # by default.
730
+ #
731
+ # - Argument +source+ must be, or be convertible to, a \String:
732
+ # - If +source+ responds to instance method +to_str+,
733
+ # <tt>source.to_str</tt> becomes the source.
734
+ # - If +source+ responds to instance method +to_io+,
735
+ # <tt>source.to_io.read</tt> becomes the source.
736
+ # - If +source+ responds to instance method +read+,
737
+ # <tt>source.read</tt> becomes the source.
738
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
739
+ # - Option +allow_blank+ specifies a truthy value.
740
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
741
+ # - Otherwise, +source+ remains the source.
742
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
743
+ # It will be called recursively with each result (depth-first order).
744
+ # See details below.
745
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
746
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
747
+ # The default options can be changed via method JSON.load_default_options=.
748
+ #
749
+ # ---
750
+ #
751
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
752
+ # <tt>parse(source, opts)</tt>; see #parse.
753
+ #
754
+ # Source for following examples:
755
+ # source = <<~JSON
756
+ # {
757
+ # "name": "Dave",
758
+ # "age" :40,
759
+ # "hats": [
760
+ # "Cattleman's",
761
+ # "Panama",
762
+ # "Tophat"
763
+ # ]
764
+ # }
765
+ # JSON
766
+ #
767
+ # Load a \String:
768
+ # ruby = JSON.load(source)
769
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
770
+ #
771
+ # Load an \IO object:
772
+ # require 'stringio'
773
+ # object = JSON.load(StringIO.new(source))
774
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
775
+ #
776
+ # Load a \File object:
777
+ # path = 't.json'
778
+ # File.write(path, source)
779
+ # File.open(path) do |file|
780
+ # JSON.load(file)
781
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
782
+ #
783
+ # ---
784
+ #
785
+ # When +proc+ is given:
786
+ # - Modifies +source+ as above.
787
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
788
+ # - Recursively calls <tt>proc(result)</tt>.
789
+ # - Returns the final result.
790
+ #
791
+ # Example:
792
+ # require 'json'
793
+ #
794
+ # # Some classes for the example.
795
+ # class Base
796
+ # def initialize(attributes)
797
+ # @attributes = attributes
798
+ # end
799
+ # end
800
+ # class User < Base; end
801
+ # class Account < Base; end
802
+ # class Admin < Base; end
803
+ # # The JSON source.
804
+ # json = <<-EOF
805
+ # {
806
+ # "users": [
807
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
808
+ # {"type": "User", "username": "john", "email": "john@example.com"}
809
+ # ],
810
+ # "accounts": [
811
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
812
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
813
+ # ],
814
+ # "admins": {"type": "Admin", "password": "0wn3d"}
815
+ # }
816
+ # EOF
817
+ # # Deserializer method.
818
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
819
+ # type = obj.is_a?(Hash) && obj["type"]
820
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
821
+ # end
822
+ # # Call to JSON.load
823
+ # ruby = JSON.load(json, proc {|obj|
824
+ # case obj
825
+ # when Hash
826
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
827
+ # when Array
828
+ # obj.map! {|v| deserialize_obj v }
829
+ # end
830
+ # obj
831
+ # })
832
+ # pp ruby
833
+ # Output:
834
+ # {"users"=>
835
+ # [#<User:0x00000000064c4c98
836
+ # @attributes=
837
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
838
+ # #<User:0x00000000064c4bd0
839
+ # @attributes=
840
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
841
+ # "accounts"=>
842
+ # [{"account"=>
843
+ # #<Account:0x00000000064c4928
844
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
845
+ # {"account"=>
846
+ # #<Account:0x00000000064c4680
847
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
848
+ # "admins"=>
849
+ # #<Admin:0x00000000064c41f8
850
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
851
+ #
852
+ def load(source, proc = nil, options = nil)
853
+ opts = if options.nil?
854
+ _load_default_options
855
+ else
856
+ _load_default_options.merge(options)
857
+ end
858
+
859
+ unless source.is_a?(String)
860
+ if source.respond_to? :to_str
861
+ source = source.to_str
862
+ elsif source.respond_to? :to_io
863
+ source = source.to_io.read
864
+ elsif source.respond_to?(:read)
865
+ source = source.read
866
+ end
867
+ end
868
+
869
+ if opts[:allow_blank] && (source.nil? || source.empty?)
870
+ source = 'null'
871
+ end
872
+
873
+ if proc
874
+ opts = opts.dup
875
+ opts[:on_load] = proc.to_proc
876
+ end
877
+
878
+ parse(source, opts)
879
+ end
880
+
881
+ # Sets or returns the default options for the JSON.dump method.
882
+ # Initially:
883
+ # opts = JSON.dump_default_options
884
+ # opts # => {:max_nesting=>false, :allow_nan=>true}
885
+ deprecated_singleton_attr_accessor :dump_default_options
886
+ @dump_default_options = {
887
+ :max_nesting => false,
888
+ :allow_nan => true,
889
+ }
890
+
891
+ # :call-seq:
892
+ # JSON.dump(obj, io = nil, limit = nil)
893
+ #
894
+ # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
895
+ #
896
+ # The default options can be changed via method JSON.dump_default_options.
897
+ #
898
+ # - Argument +io+, if given, should respond to method +write+;
899
+ # the \JSON \String is written to +io+, and +io+ is returned.
900
+ # If +io+ is not given, the \JSON \String is returned.
901
+ # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
902
+ #
903
+ # ---
904
+ #
905
+ # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
906
+ # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
907
+ # json = JSON.dump(obj)
908
+ # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
909
+ #
910
+ # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
911
+ # path = 't.json'
912
+ # File.open(path, 'w') do |file|
913
+ # JSON.dump(obj, file)
914
+ # end # => #<File:t.json (closed)>
915
+ # puts File.read(path)
916
+ # Output:
917
+ # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
918
+ def dump(obj, anIO = nil, limit = nil, kwargs = nil)
919
+ if kwargs.nil?
920
+ if limit.nil?
921
+ if anIO.is_a?(Hash)
922
+ kwargs = anIO
923
+ anIO = nil
924
+ end
925
+ elsif limit.is_a?(Hash)
926
+ kwargs = limit
927
+ limit = nil
928
+ end
929
+ end
930
+
931
+ unless anIO.nil?
932
+ if anIO.respond_to?(:to_io)
933
+ anIO = anIO.to_io
934
+ elsif limit.nil? && !anIO.respond_to?(:write)
935
+ anIO, limit = nil, anIO
936
+ end
937
+ end
938
+
939
+ opts = JSON._dump_default_options
940
+ opts = opts.merge(:max_nesting => limit) if limit
941
+ opts = opts.merge(kwargs) if kwargs
942
+
943
+ begin
944
+ State.generate(obj, opts, anIO)
945
+ rescue JSON::NestingError
946
+ raise ArgumentError, "exceed depth limit"
947
+ end
948
+ end
949
+
950
+ # :stopdoc:
951
+ # All these were meant to be deprecated circa 2009, but were just set as undocumented
952
+ # so usage still exist in the wild.
953
+ def unparse(...)
954
+ if RUBY_VERSION >= "3.0"
955
+ warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
956
+ else
957
+ warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
958
+ end
959
+ generate(...)
960
+ end
961
+ module_function :unparse
962
+
963
+ def fast_unparse(...)
964
+ if RUBY_VERSION >= "3.0"
965
+ warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
966
+ else
967
+ warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
968
+ end
969
+ generate(...)
970
+ end
971
+ module_function :fast_unparse
972
+
973
+ def pretty_unparse(...)
974
+ if RUBY_VERSION >= "3.0"
975
+ warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
976
+ else
977
+ warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
978
+ end
979
+ pretty_generate(...)
980
+ end
981
+ module_function :fast_unparse
982
+
983
+ def restore(...)
984
+ if RUBY_VERSION >= "3.0"
985
+ warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated
986
+ else
987
+ warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1
988
+ end
989
+ load(...)
990
+ end
991
+ module_function :restore
992
+
993
+ class << self
994
+ private
995
+
996
+ def const_missing(const_name)
997
+ case const_name
998
+ when :PRETTY_STATE_PROTOTYPE
999
+ if RUBY_VERSION >= "3.0"
1000
+ warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
1001
+ else
1002
+ warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
1003
+ end
1004
+ state.new(PRETTY_GENERATE_OPTIONS)
1005
+ else
1006
+ super
1007
+ end
1008
+ end
1009
+ end
1010
+ # :startdoc:
1011
+
1012
+ # JSON::Coder holds a parser and generator configuration.
1013
+ #
1014
+ # module MyApp
1015
+ # JSONC_CODER = JSON::Coder.new(
1016
+ # allow_trailing_comma: true
1017
+ # )
1018
+ # end
1019
+ #
1020
+ # MyApp::JSONC_CODER.load(document)
1021
+ #
1022
+ class Coder
1023
+ # :call-seq:
1024
+ # JSON.new(options = nil, &block)
1025
+ #
1026
+ # Argument +options+, if given, contains a \Hash of options for both parsing and generating.
1027
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
1028
+ #
1029
+ # For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
1030
+ # encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
1031
+ # \JSON counterpart:
1032
+ #
1033
+ # module MyApp
1034
+ # API_JSON_CODER = JSON::Coder.new do |object|
1035
+ # case object
1036
+ # when Time
1037
+ # object.iso8601(3)
1038
+ # else
1039
+ # object # Unknown type, will raise
1040
+ # end
1041
+ # end
1042
+ # end
1043
+ #
1044
+ # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
1045
+ #
1046
+ def initialize(options = nil, &as_json)
1047
+ if options.nil?
1048
+ options = { strict: true }
1049
+ else
1050
+ options = options.dup
1051
+ options[:strict] = true
1052
+ end
1053
+ options[:as_json] = as_json if as_json
1054
+
1055
+ @state = State.new(options).freeze
1056
+ @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options))
1057
+ end
1058
+
1059
+ # call-seq:
1060
+ # dump(object) -> String
1061
+ # dump(object, io) -> io
1062
+ #
1063
+ # Serialize the given object into a \JSON document.
1064
+ def dump(object, io = nil)
1065
+ @state.generate_new(object, io)
1066
+ end
1067
+ alias_method :generate, :dump
1068
+
1069
+ # call-seq:
1070
+ # load(string) -> Object
1071
+ #
1072
+ # Parse the given \JSON document and return an equivalent Ruby object.
1073
+ def load(source)
1074
+ @parser_config.parse(source)
1075
+ end
1076
+ alias_method :parse, :load
1077
+
1078
+ # call-seq:
1079
+ # load(path) -> Object
1080
+ #
1081
+ # Parse the given \JSON document and return an equivalent Ruby object.
1082
+ def load_file(path)
1083
+ load(File.read(path, encoding: Encoding::UTF_8))
1084
+ end
1085
+ end
1086
+ end
1087
+
1088
+ module ::Kernel
1089
+ private
1090
+
1091
+ # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
1092
+ # one line.
1093
+ def j(*objs)
1094
+ if RUBY_VERSION >= "3.0"
1095
+ warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
1096
+ else
1097
+ warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1
1098
+ end
1099
+
1100
+ objs.each do |obj|
1101
+ puts JSON.generate(obj, :allow_nan => true, :max_nesting => false)
1102
+ end
1103
+ nil
1104
+ end
1105
+
1106
+ # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
1107
+ # indentation and over many lines.
1108
+ def jj(*objs)
1109
+ if RUBY_VERSION >= "3.0"
1110
+ warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
1111
+ else
1112
+ warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1
1113
+ end
1114
+
1115
+ objs.each do |obj|
1116
+ puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false)
1117
+ end
1118
+ nil
1119
+ end
1120
+
1121
+ # If _object_ is string-like, parse the string and return the parsed result as
1122
+ # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
1123
+ # structure object and return it.
1124
+ #
1125
+ # The _opts_ argument is passed through to generate/parse respectively. See
1126
+ # generate and parse for their documentation.
1127
+ def JSON(object, opts = nil)
1128
+ JSON[object, opts]
1129
+ end
1130
+ end