json 2.7.2 → 2.9.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/BSDL +22 -0
- data/CHANGES.md +82 -17
- data/LEGAL +60 -0
- data/README.md +15 -236
- data/ext/json/ext/fbuffer/fbuffer.h +112 -85
- data/ext/json/ext/generator/extconf.rb +8 -2
- data/ext/json/ext/generator/generator.c +818 -830
- data/ext/json/ext/parser/extconf.rb +6 -27
- data/ext/json/ext/parser/parser.c +1690 -671
- data/ext/json/ext/parser/parser.rl +825 -339
- data/json.gemspec +45 -49
- data/lib/json/add/bigdecimal.rb +2 -2
- data/lib/json/add/complex.rb +1 -1
- data/lib/json/add/core.rb +1 -1
- data/lib/json/add/date.rb +1 -1
- data/lib/json/add/date_time.rb +1 -1
- data/lib/json/add/exception.rb +1 -1
- data/lib/json/add/ostruct.rb +1 -1
- data/lib/json/add/range.rb +1 -1
- data/lib/json/add/rational.rb +1 -1
- data/lib/json/add/regexp.rb +1 -1
- data/lib/json/add/struct.rb +1 -1
- data/lib/json/add/symbol.rb +1 -2
- data/lib/json/add/time.rb +3 -10
- data/lib/json/common.rb +299 -101
- data/lib/json/ext/generator/state.rb +116 -0
- data/lib/json/ext.rb +13 -5
- data/lib/json/generic_object.rb +1 -1
- data/lib/json/{pure → truffle_ruby}/generator.rb +242 -126
- data/lib/json/version.rb +3 -7
- data/lib/json.rb +16 -21
- metadata +19 -21
- data/ext/json/ext/generator/depend +0 -1
- data/ext/json/ext/generator/generator.h +0 -177
- data/ext/json/ext/parser/depend +0 -1
- data/ext/json/ext/parser/parser.h +0 -96
- data/ext/json/extconf.rb +0 -3
- data/lib/json/pure/parser.rb +0 -337
- data/lib/json/pure.rb +0 -15
- /data/{LICENSE → COPYING} +0 -0
data/lib/json/common.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'json/version'
|
3
4
|
|
4
5
|
module JSON
|
@@ -20,16 +21,19 @@ module JSON
|
|
20
21
|
# ruby = [0, 1, nil]
|
21
22
|
# JSON[ruby] # => '[0,1,null]'
|
22
23
|
def [](object, opts = {})
|
23
|
-
if object.
|
24
|
-
JSON.parse(object
|
25
|
-
|
26
|
-
|
24
|
+
if object.is_a?(String)
|
25
|
+
return JSON.parse(object, opts)
|
26
|
+
elsif object.respond_to?(:to_str)
|
27
|
+
str = object.to_str
|
28
|
+
if str.is_a?(String)
|
29
|
+
return JSON.parse(str, opts)
|
30
|
+
end
|
27
31
|
end
|
32
|
+
|
33
|
+
JSON.generate(object, opts)
|
28
34
|
end
|
29
35
|
|
30
|
-
# Returns the JSON parser class that is used by JSON.
|
31
|
-
# JSON::Ext::Parser or JSON::Pure::Parser:
|
32
|
-
# JSON.parser # => JSON::Ext::Parser
|
36
|
+
# Returns the JSON parser class that is used by JSON.
|
33
37
|
attr_reader :parser
|
34
38
|
|
35
39
|
# Set the JSON parser class _parser_ to be used by JSON.
|
@@ -44,18 +48,9 @@ module JSON
|
|
44
48
|
# level (absolute namespace path?). If there doesn't exist a constant at
|
45
49
|
# the given path, an ArgumentError is raised.
|
46
50
|
def deep_const_get(path) # :nodoc:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
when p.const_defined?(c, true) then p.const_get(c)
|
51
|
-
else
|
52
|
-
begin
|
53
|
-
p.const_missing(c)
|
54
|
-
rescue NameError => e
|
55
|
-
raise ArgumentError, "can't get const #{path}: #{e}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
51
|
+
Object.const_get(path)
|
52
|
+
rescue NameError => e
|
53
|
+
raise ArgumentError, "can't get const #{path}: #{e}"
|
59
54
|
end
|
60
55
|
|
61
56
|
# Set the module _generator_ to be used by JSON.
|
@@ -64,7 +59,7 @@ module JSON
|
|
64
59
|
@generator = generator
|
65
60
|
generator_methods = generator::GeneratorMethods
|
66
61
|
for const in generator_methods.constants
|
67
|
-
klass =
|
62
|
+
klass = const_get(const)
|
68
63
|
modul = generator_methods.const_get(const)
|
69
64
|
klass.class_eval do
|
70
65
|
instance_methods(false).each do |m|
|
@@ -101,34 +96,24 @@ module JSON
|
|
101
96
|
)
|
102
97
|
end
|
103
98
|
|
104
|
-
# Returns the JSON generator module that is used by JSON.
|
105
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
106
|
-
# JSON.generator # => JSON::Ext::Generator
|
99
|
+
# Returns the JSON generator module that is used by JSON.
|
107
100
|
attr_reader :generator
|
108
101
|
|
109
|
-
# Sets or Returns the JSON generator state class that is used by JSON.
|
110
|
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
111
|
-
# JSON.state # => JSON::Ext::Generator::State
|
102
|
+
# Sets or Returns the JSON generator state class that is used by JSON.
|
112
103
|
attr_accessor :state
|
113
104
|
end
|
114
105
|
|
115
|
-
DEFAULT_CREATE_ID = 'json_class'.freeze
|
116
|
-
private_constant :DEFAULT_CREATE_ID
|
117
|
-
|
118
|
-
CREATE_ID_TLS_KEY = "JSON.create_id".freeze
|
119
|
-
private_constant :CREATE_ID_TLS_KEY
|
120
|
-
|
121
106
|
# Sets create identifier, which is used to decide if the _json_create_
|
122
107
|
# hook of a class should be called; initial value is +json_class+:
|
123
108
|
# JSON.create_id # => 'json_class'
|
124
109
|
def self.create_id=(new_value)
|
125
|
-
Thread.current[
|
110
|
+
Thread.current[:"JSON.create_id"] = new_value.dup.freeze
|
126
111
|
end
|
127
112
|
|
128
113
|
# Returns the current create identifier.
|
129
114
|
# See also JSON.create_id=.
|
130
115
|
def self.create_id
|
131
|
-
Thread.current[
|
116
|
+
Thread.current[:"JSON.create_id"] || 'json_class'
|
132
117
|
end
|
133
118
|
|
134
119
|
NaN = 0.0/0
|
@@ -158,7 +143,23 @@ module JSON
|
|
158
143
|
# :startdoc:
|
159
144
|
|
160
145
|
# This exception is raised if a generator or unparser error occurs.
|
161
|
-
class GeneratorError < JSONError
|
146
|
+
class GeneratorError < JSONError
|
147
|
+
attr_reader :invalid_object
|
148
|
+
|
149
|
+
def initialize(message, invalid_object = nil)
|
150
|
+
super(message)
|
151
|
+
@invalid_object = invalid_object
|
152
|
+
end
|
153
|
+
|
154
|
+
def detailed_message(...)
|
155
|
+
if @invalid_object.nil?
|
156
|
+
super
|
157
|
+
else
|
158
|
+
"#{super}\nInvalid object: #{@invalid_object.inspect}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
162
163
|
# For backwards compatibility
|
163
164
|
UnparserError = GeneratorError # :nodoc:
|
164
165
|
|
@@ -196,17 +197,17 @@ module JSON
|
|
196
197
|
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
197
198
|
#
|
198
199
|
# Parses nested JSON objects:
|
199
|
-
# source =
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
#
|
209
|
-
#
|
200
|
+
# source = <<~JSON
|
201
|
+
# {
|
202
|
+
# "name": "Dave",
|
203
|
+
# "age" :40,
|
204
|
+
# "hats": [
|
205
|
+
# "Cattleman's",
|
206
|
+
# "Panama",
|
207
|
+
# "Tophat"
|
208
|
+
# ]
|
209
|
+
# }
|
210
|
+
# JSON
|
210
211
|
# ruby = JSON.parse(source)
|
211
212
|
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
212
213
|
#
|
@@ -216,8 +217,8 @@ module JSON
|
|
216
217
|
# # Raises JSON::ParserError (783: unexpected token at ''):
|
217
218
|
# JSON.parse('')
|
218
219
|
#
|
219
|
-
def parse(source, opts =
|
220
|
-
Parser.
|
220
|
+
def parse(source, opts = nil)
|
221
|
+
Parser.parse(source, opts)
|
221
222
|
end
|
222
223
|
|
223
224
|
# :call-seq:
|
@@ -246,8 +247,8 @@ module JSON
|
|
246
247
|
# parse(File.read(path), opts)
|
247
248
|
#
|
248
249
|
# See method #parse.
|
249
|
-
def load_file(filespec, opts =
|
250
|
-
parse(File.read(filespec), opts)
|
250
|
+
def load_file(filespec, opts = nil)
|
251
|
+
parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
|
251
252
|
end
|
252
253
|
|
253
254
|
# :call-seq:
|
@@ -258,7 +259,7 @@ module JSON
|
|
258
259
|
#
|
259
260
|
# See method #parse!
|
260
261
|
def load_file!(filespec, opts = {})
|
261
|
-
parse!(File.read(filespec), opts)
|
262
|
+
parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
|
262
263
|
end
|
263
264
|
|
264
265
|
# :call-seq:
|
@@ -299,11 +300,10 @@ module JSON
|
|
299
300
|
#
|
300
301
|
def generate(obj, opts = nil)
|
301
302
|
if State === opts
|
302
|
-
|
303
|
+
opts.generate(obj)
|
303
304
|
else
|
304
|
-
|
305
|
+
State.generate(obj, opts, nil)
|
305
306
|
end
|
306
|
-
state.generate(obj)
|
307
307
|
end
|
308
308
|
|
309
309
|
# :stopdoc:
|
@@ -396,6 +396,20 @@ module JSON
|
|
396
396
|
module_function :pretty_unparse
|
397
397
|
# :startdoc:
|
398
398
|
|
399
|
+
class << self
|
400
|
+
# Sets or returns default options for the JSON.unsafe_load method.
|
401
|
+
# Initially:
|
402
|
+
# opts = JSON.load_default_options
|
403
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
404
|
+
attr_accessor :unsafe_load_default_options
|
405
|
+
end
|
406
|
+
self.unsafe_load_default_options = {
|
407
|
+
:max_nesting => false,
|
408
|
+
:allow_nan => true,
|
409
|
+
:allow_blank => true,
|
410
|
+
:create_additions => true,
|
411
|
+
}
|
412
|
+
|
399
413
|
class << self
|
400
414
|
# Sets or returns default options for the JSON.load method.
|
401
415
|
# Initially:
|
@@ -404,17 +418,179 @@ module JSON
|
|
404
418
|
attr_accessor :load_default_options
|
405
419
|
end
|
406
420
|
self.load_default_options = {
|
407
|
-
:max_nesting => false,
|
408
421
|
:allow_nan => true,
|
409
|
-
:allow_blank
|
410
|
-
:create_additions =>
|
422
|
+
:allow_blank => true,
|
423
|
+
:create_additions => nil,
|
411
424
|
}
|
425
|
+
# :call-seq:
|
426
|
+
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
|
427
|
+
#
|
428
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
429
|
+
#
|
430
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
431
|
+
# like from your own database server or clients under your control, it could
|
432
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
433
|
+
#
|
434
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
435
|
+
# - If +source+ responds to instance method +to_str+,
|
436
|
+
# <tt>source.to_str</tt> becomes the source.
|
437
|
+
# - If +source+ responds to instance method +to_io+,
|
438
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
439
|
+
# - If +source+ responds to instance method +read+,
|
440
|
+
# <tt>source.read</tt> becomes the source.
|
441
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
442
|
+
# - Option +allow_blank+ specifies a truthy value.
|
443
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
444
|
+
# - Otherwise, +source+ remains the source.
|
445
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
446
|
+
# It will be called recursively with each result (depth-first order).
|
447
|
+
# See details below.
|
448
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
449
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
450
|
+
# The default options can be changed via method JSON.unsafe_load_default_options=.
|
451
|
+
#
|
452
|
+
# ---
|
453
|
+
#
|
454
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
455
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
456
|
+
#
|
457
|
+
# Source for following examples:
|
458
|
+
# source = <<~JSON
|
459
|
+
# {
|
460
|
+
# "name": "Dave",
|
461
|
+
# "age" :40,
|
462
|
+
# "hats": [
|
463
|
+
# "Cattleman's",
|
464
|
+
# "Panama",
|
465
|
+
# "Tophat"
|
466
|
+
# ]
|
467
|
+
# }
|
468
|
+
# JSON
|
469
|
+
#
|
470
|
+
# Load a \String:
|
471
|
+
# ruby = JSON.unsafe_load(source)
|
472
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
473
|
+
#
|
474
|
+
# Load an \IO object:
|
475
|
+
# require 'stringio'
|
476
|
+
# object = JSON.unsafe_load(StringIO.new(source))
|
477
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
478
|
+
#
|
479
|
+
# Load a \File object:
|
480
|
+
# path = 't.json'
|
481
|
+
# File.write(path, source)
|
482
|
+
# File.open(path) do |file|
|
483
|
+
# JSON.unsafe_load(file)
|
484
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
485
|
+
#
|
486
|
+
# ---
|
487
|
+
#
|
488
|
+
# When +proc+ is given:
|
489
|
+
# - Modifies +source+ as above.
|
490
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
491
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
492
|
+
# - Returns the final result.
|
493
|
+
#
|
494
|
+
# Example:
|
495
|
+
# require 'json'
|
496
|
+
#
|
497
|
+
# # Some classes for the example.
|
498
|
+
# class Base
|
499
|
+
# def initialize(attributes)
|
500
|
+
# @attributes = attributes
|
501
|
+
# end
|
502
|
+
# end
|
503
|
+
# class User < Base; end
|
504
|
+
# class Account < Base; end
|
505
|
+
# class Admin < Base; end
|
506
|
+
# # The JSON source.
|
507
|
+
# json = <<-EOF
|
508
|
+
# {
|
509
|
+
# "users": [
|
510
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
511
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
512
|
+
# ],
|
513
|
+
# "accounts": [
|
514
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
515
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
516
|
+
# ],
|
517
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
518
|
+
# }
|
519
|
+
# EOF
|
520
|
+
# # Deserializer method.
|
521
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
522
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
523
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
524
|
+
# end
|
525
|
+
# # Call to JSON.unsafe_load
|
526
|
+
# ruby = JSON.unsafe_load(json, proc {|obj|
|
527
|
+
# case obj
|
528
|
+
# when Hash
|
529
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
530
|
+
# when Array
|
531
|
+
# obj.map! {|v| deserialize_obj v }
|
532
|
+
# end
|
533
|
+
# })
|
534
|
+
# pp ruby
|
535
|
+
# Output:
|
536
|
+
# {"users"=>
|
537
|
+
# [#<User:0x00000000064c4c98
|
538
|
+
# @attributes=
|
539
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
540
|
+
# #<User:0x00000000064c4bd0
|
541
|
+
# @attributes=
|
542
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
543
|
+
# "accounts"=>
|
544
|
+
# [{"account"=>
|
545
|
+
# #<Account:0x00000000064c4928
|
546
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
547
|
+
# {"account"=>
|
548
|
+
# #<Account:0x00000000064c4680
|
549
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
550
|
+
# "admins"=>
|
551
|
+
# #<Admin:0x00000000064c41f8
|
552
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
553
|
+
#
|
554
|
+
def unsafe_load(source, proc = nil, options = nil)
|
555
|
+
opts = if options.nil?
|
556
|
+
unsafe_load_default_options
|
557
|
+
else
|
558
|
+
unsafe_load_default_options.merge(options)
|
559
|
+
end
|
560
|
+
|
561
|
+
unless source.is_a?(String)
|
562
|
+
if source.respond_to? :to_str
|
563
|
+
source = source.to_str
|
564
|
+
elsif source.respond_to? :to_io
|
565
|
+
source = source.to_io.read
|
566
|
+
elsif source.respond_to?(:read)
|
567
|
+
source = source.read
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
if opts[:allow_blank] && (source.nil? || source.empty?)
|
572
|
+
source = 'null'
|
573
|
+
end
|
574
|
+
result = parse(source, opts)
|
575
|
+
recurse_proc(result, &proc) if proc
|
576
|
+
result
|
577
|
+
end
|
412
578
|
|
413
579
|
# :call-seq:
|
414
580
|
# JSON.load(source, proc = nil, options = {}) -> object
|
415
581
|
#
|
416
582
|
# Returns the Ruby objects created by parsing the given +source+.
|
417
583
|
#
|
584
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
585
|
+
# like from your own database server or clients under your control, it could
|
586
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
587
|
+
# If you must use it, use JSON.unsafe_load instead to make it clear.
|
588
|
+
#
|
589
|
+
# Since JSON version 2.8.0, `load` emits a deprecation warning when a
|
590
|
+
# non native type is deserialized, without `create_additions` being explicitly
|
591
|
+
# enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
|
592
|
+
# by default.
|
593
|
+
#
|
418
594
|
# - Argument +source+ must be, or be convertible to, a \String:
|
419
595
|
# - If +source+ responds to instance method +to_str+,
|
420
596
|
# <tt>source.to_str</tt> becomes the source.
|
@@ -429,9 +605,6 @@ module JSON
|
|
429
605
|
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
430
606
|
# It will be called recursively with each result (depth-first order).
|
431
607
|
# See details below.
|
432
|
-
# BEWARE: This method is meant to serialise data from trusted user input,
|
433
|
-
# like from your own database server or clients under your control, it could
|
434
|
-
# be dangerous to allow untrusted users to pass JSON sources into it.
|
435
608
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
436
609
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
437
610
|
# The default options can be changed via method JSON.load_default_options=.
|
@@ -442,17 +615,17 @@ module JSON
|
|
442
615
|
# <tt>parse(source, opts)</tt>; see #parse.
|
443
616
|
#
|
444
617
|
# Source for following examples:
|
445
|
-
# source =
|
446
|
-
#
|
447
|
-
#
|
448
|
-
#
|
449
|
-
#
|
450
|
-
#
|
451
|
-
#
|
452
|
-
#
|
453
|
-
#
|
454
|
-
#
|
455
|
-
#
|
618
|
+
# source = <<~JSON
|
619
|
+
# {
|
620
|
+
# "name": "Dave",
|
621
|
+
# "age" :40,
|
622
|
+
# "hats": [
|
623
|
+
# "Cattleman's",
|
624
|
+
# "Panama",
|
625
|
+
# "Tophat"
|
626
|
+
# ]
|
627
|
+
# }
|
628
|
+
# JSON
|
456
629
|
#
|
457
630
|
# Load a \String:
|
458
631
|
# ruby = JSON.load(source)
|
@@ -538,15 +711,23 @@ module JSON
|
|
538
711
|
# #<Admin:0x00000000064c41f8
|
539
712
|
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
540
713
|
#
|
541
|
-
def load(source, proc = nil, options =
|
542
|
-
opts =
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
source = source.to_io.read
|
547
|
-
elsif source.respond_to?(:read)
|
548
|
-
source = source.read
|
714
|
+
def load(source, proc = nil, options = nil)
|
715
|
+
opts = if options.nil?
|
716
|
+
load_default_options
|
717
|
+
else
|
718
|
+
load_default_options.merge(options)
|
549
719
|
end
|
720
|
+
|
721
|
+
unless source.is_a?(String)
|
722
|
+
if source.respond_to? :to_str
|
723
|
+
source = source.to_str
|
724
|
+
elsif source.respond_to? :to_io
|
725
|
+
source = source.to_io.read
|
726
|
+
elsif source.respond_to?(:read)
|
727
|
+
source = source.read
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
550
731
|
if opts[:allow_blank] && (source.nil? || source.empty?)
|
551
732
|
source = 'null'
|
552
733
|
end
|
@@ -576,13 +757,12 @@ module JSON
|
|
576
757
|
# Sets or returns the default options for the JSON.dump method.
|
577
758
|
# Initially:
|
578
759
|
# opts = JSON.dump_default_options
|
579
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true
|
760
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true}
|
580
761
|
attr_accessor :dump_default_options
|
581
762
|
end
|
582
763
|
self.dump_default_options = {
|
583
764
|
:max_nesting => false,
|
584
765
|
:allow_nan => true,
|
585
|
-
:script_safe => false,
|
586
766
|
}
|
587
767
|
|
588
768
|
# :call-seq:
|
@@ -613,26 +793,39 @@ module JSON
|
|
613
793
|
# Output:
|
614
794
|
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
615
795
|
def dump(obj, anIO = nil, limit = nil, kwargs = nil)
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
796
|
+
if kwargs.nil?
|
797
|
+
if limit.nil?
|
798
|
+
if anIO.is_a?(Hash)
|
799
|
+
kwargs = anIO
|
800
|
+
anIO = nil
|
801
|
+
end
|
802
|
+
elsif limit.is_a?(Hash)
|
803
|
+
kwargs = limit
|
804
|
+
limit = nil
|
805
|
+
end
|
623
806
|
end
|
807
|
+
|
808
|
+
unless anIO.nil?
|
809
|
+
if anIO.respond_to?(:to_io)
|
810
|
+
anIO = anIO.to_io
|
811
|
+
elsif limit.nil? && !anIO.respond_to?(:write)
|
812
|
+
anIO, limit = nil, anIO
|
813
|
+
end
|
814
|
+
end
|
815
|
+
|
624
816
|
opts = JSON.dump_default_options
|
625
817
|
opts = opts.merge(:max_nesting => limit) if limit
|
626
818
|
opts = merge_dump_options(opts, **kwargs) if kwargs
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
819
|
+
|
820
|
+
begin
|
821
|
+
if State === opts
|
822
|
+
opts.generate(obj, anIO)
|
823
|
+
else
|
824
|
+
State.generate(obj, opts, anIO)
|
825
|
+
end
|
826
|
+
rescue JSON::NestingError
|
827
|
+
raise ArgumentError, "exceed depth limit"
|
633
828
|
end
|
634
|
-
rescue JSON::NestingError
|
635
|
-
raise ArgumentError, "exceed depth limit"
|
636
829
|
end
|
637
830
|
|
638
831
|
# Encodes string using String.encode.
|
@@ -678,11 +871,16 @@ module ::Kernel
|
|
678
871
|
# The _opts_ argument is passed through to generate/parse respectively. See
|
679
872
|
# generate and parse for their documentation.
|
680
873
|
def JSON(object, *args)
|
681
|
-
if object.
|
682
|
-
JSON.parse(object
|
683
|
-
|
684
|
-
|
874
|
+
if object.is_a?(String)
|
875
|
+
return JSON.parse(object, args.first)
|
876
|
+
elsif object.respond_to?(:to_str)
|
877
|
+
str = object.to_str
|
878
|
+
if str.is_a?(String)
|
879
|
+
return JSON.parse(object.to_str, args.first)
|
880
|
+
end
|
685
881
|
end
|
882
|
+
|
883
|
+
JSON.generate(object, args.first)
|
686
884
|
end
|
687
885
|
end
|
688
886
|
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSON
|
4
|
+
module Ext
|
5
|
+
module Generator
|
6
|
+
class State
|
7
|
+
# call-seq: new(opts = {})
|
8
|
+
#
|
9
|
+
# Instantiates a new State object, configured by _opts_.
|
10
|
+
#
|
11
|
+
# _opts_ can have the following keys:
|
12
|
+
#
|
13
|
+
# * *indent*: a string used to indent levels (default: ''),
|
14
|
+
# * *space*: a string that is put after, a : or , delimiter (default: ''),
|
15
|
+
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
|
16
|
+
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
|
17
|
+
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
|
18
|
+
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
19
|
+
# generated, otherwise an exception is thrown, if these values are
|
20
|
+
# encountered. This options defaults to false.
|
21
|
+
# * *ascii_only*: true if only ASCII characters should be generated. This
|
22
|
+
# option defaults to false.
|
23
|
+
# * *buffer_initial_length*: sets the initial length of the generator's
|
24
|
+
# internal buffer.
|
25
|
+
def initialize(opts = nil)
|
26
|
+
if opts && !opts.empty?
|
27
|
+
configure(opts)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# call-seq: configure(opts)
|
32
|
+
#
|
33
|
+
# Configure this State instance with the Hash _opts_, and return
|
34
|
+
# itself.
|
35
|
+
def configure(opts)
|
36
|
+
unless opts.is_a?(Hash)
|
37
|
+
if opts.respond_to?(:to_hash)
|
38
|
+
opts = opts.to_hash
|
39
|
+
elsif opts.respond_to?(:to_h)
|
40
|
+
opts = opts.to_h
|
41
|
+
else
|
42
|
+
raise TypeError, "can't convert #{opts.class} into Hash"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
_configure(opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :merge, :configure
|
49
|
+
|
50
|
+
# call-seq:
|
51
|
+
# generate(obj) -> String
|
52
|
+
# generate(obj, anIO) -> anIO
|
53
|
+
#
|
54
|
+
# Generates a valid JSON document from object +obj+ and returns the
|
55
|
+
# result. If no valid JSON document can be created this method raises a
|
56
|
+
# GeneratorError exception.
|
57
|
+
def generate(obj, io = nil)
|
58
|
+
_generate(obj, io)
|
59
|
+
end
|
60
|
+
|
61
|
+
# call-seq: to_h
|
62
|
+
#
|
63
|
+
# Returns the configuration instance variables as a hash, that can be
|
64
|
+
# passed to the configure method.
|
65
|
+
def to_h
|
66
|
+
result = {
|
67
|
+
indent: indent,
|
68
|
+
space: space,
|
69
|
+
space_before: space_before,
|
70
|
+
object_nl: object_nl,
|
71
|
+
array_nl: array_nl,
|
72
|
+
allow_nan: allow_nan?,
|
73
|
+
ascii_only: ascii_only?,
|
74
|
+
max_nesting: max_nesting,
|
75
|
+
script_safe: script_safe?,
|
76
|
+
strict: strict?,
|
77
|
+
depth: depth,
|
78
|
+
buffer_initial_length: buffer_initial_length,
|
79
|
+
}
|
80
|
+
|
81
|
+
instance_variables.each do |iv|
|
82
|
+
iv = iv.to_s[1..-1]
|
83
|
+
result[iv.to_sym] = self[iv]
|
84
|
+
end
|
85
|
+
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :to_hash, :to_h
|
90
|
+
|
91
|
+
# call-seq: [](name)
|
92
|
+
#
|
93
|
+
# Returns the value returned by method +name+.
|
94
|
+
def [](name)
|
95
|
+
if respond_to?(name)
|
96
|
+
__send__(name)
|
97
|
+
else
|
98
|
+
instance_variable_get("@#{name}") if
|
99
|
+
instance_variables.include?("@#{name}".to_sym) # avoid warning
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# call-seq: []=(name, value)
|
104
|
+
#
|
105
|
+
# Sets the attribute name to value.
|
106
|
+
def []=(name, value)
|
107
|
+
if respond_to?(name_writer = "#{name}=")
|
108
|
+
__send__ name_writer, value
|
109
|
+
else
|
110
|
+
instance_variable_set "@#{name}", value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|