json 2.7.3 → 2.10.1
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 +61 -0
- data/LEGAL +0 -52
- data/README.md +61 -57
- data/ext/json/ext/fbuffer/fbuffer.h +123 -64
- data/ext/json/ext/generator/generator.c +795 -474
- data/ext/json/ext/parser/extconf.rb +5 -27
- data/ext/json/ext/parser/parser.c +1164 -1923
- data/json.gemspec +7 -5
- data/lib/json/add/bigdecimal.rb +1 -1
- data/lib/json/add/symbol.rb +7 -2
- data/lib/json/common.rb +341 -77
- data/lib/json/ext/generator/state.rb +2 -31
- data/lib/json/ext.rb +28 -11
- data/lib/json/{pure → truffle_ruby}/generator.rb +264 -154
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +15 -20
- metadata +8 -17
- data/ext/json/ext/generator/generator.h +0 -129
- data/ext/json/ext/parser/parser.h +0 -60
- data/ext/json/ext/parser/parser.rl +0 -997
- data/lib/json/pure/parser.rb +0 -331
- data/lib/json/pure.rb +0 -16
data/json.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line|
|
2
4
|
/^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1
|
3
5
|
end rescue nil
|
@@ -9,17 +11,16 @@ spec = Gem::Specification.new do |s|
|
|
9
11
|
s.version = version
|
10
12
|
|
11
13
|
s.summary = "JSON Implementation for Ruby"
|
12
|
-
s.homepage = "https://
|
14
|
+
s.homepage = "https://github.com/ruby/json"
|
13
15
|
s.metadata = {
|
14
16
|
'bug_tracker_uri' => 'https://github.com/ruby/json/issues',
|
15
17
|
'changelog_uri' => 'https://github.com/ruby/json/blob/master/CHANGES.md',
|
16
|
-
'documentation_uri' => 'https://ruby.
|
18
|
+
'documentation_uri' => 'https://docs.ruby-lang.org/en/master/JSON.html',
|
17
19
|
'homepage_uri' => s.homepage,
|
18
20
|
'source_code_uri' => 'https://github.com/ruby/json',
|
19
|
-
'wiki_uri' => 'https://github.com/ruby/json/wiki'
|
20
21
|
}
|
21
22
|
|
22
|
-
s.required_ruby_version = Gem::Requirement.new(">= 2.
|
23
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.7")
|
23
24
|
|
24
25
|
if java_ext
|
25
26
|
s.description = "A JSON implementation as a JRuby extension."
|
@@ -48,9 +49,10 @@ spec = Gem::Specification.new do |s|
|
|
48
49
|
|
49
50
|
if java_ext
|
50
51
|
s.platform = 'java'
|
52
|
+
s.files += Dir["lib/json/ext/**/*.jar"]
|
51
53
|
else
|
52
54
|
s.extensions = Dir["ext/json/**/extconf.rb"]
|
53
|
-
s.files += Dir["ext/json/**/*.{c,h
|
55
|
+
s.files += Dir["ext/json/**/*.{c,h}"]
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
data/lib/json/add/bigdecimal.rb
CHANGED
data/lib/json/add/symbol.rb
CHANGED
@@ -36,8 +36,13 @@ class Symbol
|
|
36
36
|
#
|
37
37
|
# # {"json_class":"Symbol","s":"foo"}
|
38
38
|
#
|
39
|
-
def to_json(*a)
|
40
|
-
|
39
|
+
def to_json(state = nil, *a)
|
40
|
+
state = ::JSON::State.from_state(state)
|
41
|
+
if state.strict?
|
42
|
+
super
|
43
|
+
else
|
44
|
+
as_json.to_json(state, *a)
|
45
|
+
end
|
41
46
|
end
|
42
47
|
|
43
48
|
# See #as_json.
|
data/lib/json/common.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'json/version'
|
3
4
|
|
4
5
|
module JSON
|
@@ -25,16 +26,14 @@ module JSON
|
|
25
26
|
elsif object.respond_to?(:to_str)
|
26
27
|
str = object.to_str
|
27
28
|
if str.is_a?(String)
|
28
|
-
return JSON.parse(
|
29
|
+
return JSON.parse(str, opts)
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
33
|
JSON.generate(object, opts)
|
33
34
|
end
|
34
35
|
|
35
|
-
# Returns the JSON parser class that is used by JSON.
|
36
|
-
# JSON::Ext::Parser or JSON::Pure::Parser:
|
37
|
-
# JSON.parser # => JSON::Ext::Parser
|
36
|
+
# Returns the JSON parser class that is used by JSON.
|
38
37
|
attr_reader :parser
|
39
38
|
|
40
39
|
# Set the JSON parser class _parser_ to be used by JSON.
|
@@ -49,18 +48,9 @@ module JSON
|
|
49
48
|
# level (absolute namespace path?). If there doesn't exist a constant at
|
50
49
|
# the given path, an ArgumentError is raised.
|
51
50
|
def deep_const_get(path) # :nodoc:
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
when p.const_defined?(c, true) then p.const_get(c)
|
56
|
-
else
|
57
|
-
begin
|
58
|
-
p.const_missing(c)
|
59
|
-
rescue NameError => e
|
60
|
-
raise ArgumentError, "can't get const #{path}: #{e}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
51
|
+
Object.const_get(path)
|
52
|
+
rescue NameError => e
|
53
|
+
raise ArgumentError, "can't get const #{path}: #{e}"
|
64
54
|
end
|
65
55
|
|
66
56
|
# Set the module _generator_ to be used by JSON.
|
@@ -69,7 +59,7 @@ module JSON
|
|
69
59
|
@generator = generator
|
70
60
|
generator_methods = generator::GeneratorMethods
|
71
61
|
for const in generator_methods.constants
|
72
|
-
klass =
|
62
|
+
klass = const_get(const)
|
73
63
|
modul = generator_methods.const_get(const)
|
74
64
|
klass.class_eval do
|
75
65
|
instance_methods(false).each do |m|
|
@@ -106,14 +96,10 @@ module JSON
|
|
106
96
|
)
|
107
97
|
end
|
108
98
|
|
109
|
-
# Returns the JSON generator module that is used by JSON.
|
110
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
111
|
-
# JSON.generator # => JSON::Ext::Generator
|
99
|
+
# Returns the JSON generator module that is used by JSON.
|
112
100
|
attr_reader :generator
|
113
101
|
|
114
|
-
# Sets or Returns the JSON generator state class that is used by JSON.
|
115
|
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
116
|
-
# JSON.state # => JSON::Ext::Generator::State
|
102
|
+
# Sets or Returns the JSON generator state class that is used by JSON.
|
117
103
|
attr_accessor :state
|
118
104
|
end
|
119
105
|
|
@@ -157,7 +143,23 @@ module JSON
|
|
157
143
|
# :startdoc:
|
158
144
|
|
159
145
|
# This exception is raised if a generator or unparser error occurs.
|
160
|
-
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
|
+
|
161
163
|
# For backwards compatibility
|
162
164
|
UnparserError = GeneratorError # :nodoc:
|
163
165
|
|
@@ -165,6 +167,30 @@ module JSON
|
|
165
167
|
# system. Usually this means that the iconv library is not installed.
|
166
168
|
class MissingUnicodeSupport < JSONError; end
|
167
169
|
|
170
|
+
# Fragment of JSON document that is to be included as is:
|
171
|
+
# fragment = JSON::Fragment.new("[1, 2, 3]")
|
172
|
+
# JSON.generate({ count: 3, items: fragments })
|
173
|
+
#
|
174
|
+
# This allows to easily assemble multiple JSON fragments that have
|
175
|
+
# been persisted somewhere without having to parse them nor resorting
|
176
|
+
# to string interpolation.
|
177
|
+
#
|
178
|
+
# Note: no validation is performed on the provided string. It is the
|
179
|
+
# responsability of the caller to ensure the string contains valid JSON.
|
180
|
+
Fragment = Struct.new(:json) do
|
181
|
+
def initialize(json)
|
182
|
+
unless string = String.try_convert(json)
|
183
|
+
raise TypeError, " no implicit conversion of #{json.class} into String"
|
184
|
+
end
|
185
|
+
|
186
|
+
super(string)
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_json(state = nil, *)
|
190
|
+
json
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
168
194
|
module_function
|
169
195
|
|
170
196
|
# :call-seq:
|
@@ -195,17 +221,17 @@ module JSON
|
|
195
221
|
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
196
222
|
#
|
197
223
|
# Parses nested JSON objects:
|
198
|
-
# source =
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
#
|
224
|
+
# source = <<~JSON
|
225
|
+
# {
|
226
|
+
# "name": "Dave",
|
227
|
+
# "age" :40,
|
228
|
+
# "hats": [
|
229
|
+
# "Cattleman's",
|
230
|
+
# "Panama",
|
231
|
+
# "Tophat"
|
232
|
+
# ]
|
233
|
+
# }
|
234
|
+
# JSON
|
209
235
|
# ruby = JSON.parse(source)
|
210
236
|
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
211
237
|
#
|
@@ -216,11 +242,7 @@ module JSON
|
|
216
242
|
# JSON.parse('')
|
217
243
|
#
|
218
244
|
def parse(source, opts = nil)
|
219
|
-
|
220
|
-
Parser.new(source).parse
|
221
|
-
else
|
222
|
-
Parser.new(source, opts).parse
|
223
|
-
end
|
245
|
+
Parser.parse(source, opts)
|
224
246
|
end
|
225
247
|
|
226
248
|
# :call-seq:
|
@@ -234,12 +256,13 @@ module JSON
|
|
234
256
|
# - Option +max_nesting+, if not provided, defaults to +false+,
|
235
257
|
# which disables checking for nesting depth.
|
236
258
|
# - Option +allow_nan+, if not provided, defaults to +true+.
|
237
|
-
def parse!(source, opts =
|
238
|
-
|
259
|
+
def parse!(source, opts = nil)
|
260
|
+
options = {
|
239
261
|
:max_nesting => false,
|
240
262
|
:allow_nan => true
|
241
|
-
}
|
242
|
-
|
263
|
+
}
|
264
|
+
options.merge!(opts) if opts
|
265
|
+
Parser.new(source, options).parse
|
243
266
|
end
|
244
267
|
|
245
268
|
# :call-seq:
|
@@ -249,8 +272,8 @@ module JSON
|
|
249
272
|
# parse(File.read(path), opts)
|
250
273
|
#
|
251
274
|
# See method #parse.
|
252
|
-
def load_file(filespec, opts =
|
253
|
-
parse(File.read(filespec), opts)
|
275
|
+
def load_file(filespec, opts = nil)
|
276
|
+
parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
|
254
277
|
end
|
255
278
|
|
256
279
|
# :call-seq:
|
@@ -260,8 +283,8 @@ module JSON
|
|
260
283
|
# JSON.parse!(File.read(path, opts))
|
261
284
|
#
|
262
285
|
# See method #parse!
|
263
|
-
def load_file!(filespec, opts =
|
264
|
-
parse!(File.read(filespec), opts)
|
286
|
+
def load_file!(filespec, opts = nil)
|
287
|
+
parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
|
265
288
|
end
|
266
289
|
|
267
290
|
# :call-seq:
|
@@ -302,11 +325,10 @@ module JSON
|
|
302
325
|
#
|
303
326
|
def generate(obj, opts = nil)
|
304
327
|
if State === opts
|
305
|
-
|
328
|
+
opts.generate(obj)
|
306
329
|
else
|
307
|
-
|
330
|
+
State.generate(obj, opts, nil)
|
308
331
|
end
|
309
|
-
state.generate(obj)
|
310
332
|
end
|
311
333
|
|
312
334
|
# :stopdoc:
|
@@ -399,6 +421,20 @@ module JSON
|
|
399
421
|
module_function :pretty_unparse
|
400
422
|
# :startdoc:
|
401
423
|
|
424
|
+
class << self
|
425
|
+
# Sets or returns default options for the JSON.unsafe_load method.
|
426
|
+
# Initially:
|
427
|
+
# opts = JSON.load_default_options
|
428
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
429
|
+
attr_accessor :unsafe_load_default_options
|
430
|
+
end
|
431
|
+
self.unsafe_load_default_options = {
|
432
|
+
:max_nesting => false,
|
433
|
+
:allow_nan => true,
|
434
|
+
:allow_blank => true,
|
435
|
+
:create_additions => true,
|
436
|
+
}
|
437
|
+
|
402
438
|
class << self
|
403
439
|
# Sets or returns default options for the JSON.load method.
|
404
440
|
# Initially:
|
@@ -407,17 +443,179 @@ module JSON
|
|
407
443
|
attr_accessor :load_default_options
|
408
444
|
end
|
409
445
|
self.load_default_options = {
|
410
|
-
:max_nesting => false,
|
411
446
|
:allow_nan => true,
|
412
447
|
:allow_blank => true,
|
413
|
-
:create_additions =>
|
448
|
+
:create_additions => nil,
|
414
449
|
}
|
450
|
+
# :call-seq:
|
451
|
+
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
|
452
|
+
#
|
453
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
454
|
+
#
|
455
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
456
|
+
# like from your own database server or clients under your control, it could
|
457
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
458
|
+
#
|
459
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
460
|
+
# - If +source+ responds to instance method +to_str+,
|
461
|
+
# <tt>source.to_str</tt> becomes the source.
|
462
|
+
# - If +source+ responds to instance method +to_io+,
|
463
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
464
|
+
# - If +source+ responds to instance method +read+,
|
465
|
+
# <tt>source.read</tt> becomes the source.
|
466
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
467
|
+
# - Option +allow_blank+ specifies a truthy value.
|
468
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
469
|
+
# - Otherwise, +source+ remains the source.
|
470
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
471
|
+
# It will be called recursively with each result (depth-first order).
|
472
|
+
# See details below.
|
473
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
474
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
475
|
+
# The default options can be changed via method JSON.unsafe_load_default_options=.
|
476
|
+
#
|
477
|
+
# ---
|
478
|
+
#
|
479
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
480
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
481
|
+
#
|
482
|
+
# Source for following examples:
|
483
|
+
# source = <<~JSON
|
484
|
+
# {
|
485
|
+
# "name": "Dave",
|
486
|
+
# "age" :40,
|
487
|
+
# "hats": [
|
488
|
+
# "Cattleman's",
|
489
|
+
# "Panama",
|
490
|
+
# "Tophat"
|
491
|
+
# ]
|
492
|
+
# }
|
493
|
+
# JSON
|
494
|
+
#
|
495
|
+
# Load a \String:
|
496
|
+
# ruby = JSON.unsafe_load(source)
|
497
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
498
|
+
#
|
499
|
+
# Load an \IO object:
|
500
|
+
# require 'stringio'
|
501
|
+
# object = JSON.unsafe_load(StringIO.new(source))
|
502
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
503
|
+
#
|
504
|
+
# Load a \File object:
|
505
|
+
# path = 't.json'
|
506
|
+
# File.write(path, source)
|
507
|
+
# File.open(path) do |file|
|
508
|
+
# JSON.unsafe_load(file)
|
509
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
510
|
+
#
|
511
|
+
# ---
|
512
|
+
#
|
513
|
+
# When +proc+ is given:
|
514
|
+
# - Modifies +source+ as above.
|
515
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
516
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
517
|
+
# - Returns the final result.
|
518
|
+
#
|
519
|
+
# Example:
|
520
|
+
# require 'json'
|
521
|
+
#
|
522
|
+
# # Some classes for the example.
|
523
|
+
# class Base
|
524
|
+
# def initialize(attributes)
|
525
|
+
# @attributes = attributes
|
526
|
+
# end
|
527
|
+
# end
|
528
|
+
# class User < Base; end
|
529
|
+
# class Account < Base; end
|
530
|
+
# class Admin < Base; end
|
531
|
+
# # The JSON source.
|
532
|
+
# json = <<-EOF
|
533
|
+
# {
|
534
|
+
# "users": [
|
535
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
536
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
537
|
+
# ],
|
538
|
+
# "accounts": [
|
539
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
540
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
541
|
+
# ],
|
542
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
543
|
+
# }
|
544
|
+
# EOF
|
545
|
+
# # Deserializer method.
|
546
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
547
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
548
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
549
|
+
# end
|
550
|
+
# # Call to JSON.unsafe_load
|
551
|
+
# ruby = JSON.unsafe_load(json, proc {|obj|
|
552
|
+
# case obj
|
553
|
+
# when Hash
|
554
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
555
|
+
# when Array
|
556
|
+
# obj.map! {|v| deserialize_obj v }
|
557
|
+
# end
|
558
|
+
# })
|
559
|
+
# pp ruby
|
560
|
+
# Output:
|
561
|
+
# {"users"=>
|
562
|
+
# [#<User:0x00000000064c4c98
|
563
|
+
# @attributes=
|
564
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
565
|
+
# #<User:0x00000000064c4bd0
|
566
|
+
# @attributes=
|
567
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
568
|
+
# "accounts"=>
|
569
|
+
# [{"account"=>
|
570
|
+
# #<Account:0x00000000064c4928
|
571
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
572
|
+
# {"account"=>
|
573
|
+
# #<Account:0x00000000064c4680
|
574
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
575
|
+
# "admins"=>
|
576
|
+
# #<Admin:0x00000000064c41f8
|
577
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
578
|
+
#
|
579
|
+
def unsafe_load(source, proc = nil, options = nil)
|
580
|
+
opts = if options.nil?
|
581
|
+
unsafe_load_default_options
|
582
|
+
else
|
583
|
+
unsafe_load_default_options.merge(options)
|
584
|
+
end
|
585
|
+
|
586
|
+
unless source.is_a?(String)
|
587
|
+
if source.respond_to? :to_str
|
588
|
+
source = source.to_str
|
589
|
+
elsif source.respond_to? :to_io
|
590
|
+
source = source.to_io.read
|
591
|
+
elsif source.respond_to?(:read)
|
592
|
+
source = source.read
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
if opts[:allow_blank] && (source.nil? || source.empty?)
|
597
|
+
source = 'null'
|
598
|
+
end
|
599
|
+
result = parse(source, opts)
|
600
|
+
recurse_proc(result, &proc) if proc
|
601
|
+
result
|
602
|
+
end
|
415
603
|
|
416
604
|
# :call-seq:
|
417
605
|
# JSON.load(source, proc = nil, options = {}) -> object
|
418
606
|
#
|
419
607
|
# Returns the Ruby objects created by parsing the given +source+.
|
420
608
|
#
|
609
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
610
|
+
# like from your own database server or clients under your control, it could
|
611
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
612
|
+
# If you must use it, use JSON.unsafe_load instead to make it clear.
|
613
|
+
#
|
614
|
+
# Since JSON version 2.8.0, `load` emits a deprecation warning when a
|
615
|
+
# non native type is deserialized, without `create_additions` being explicitly
|
616
|
+
# enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
|
617
|
+
# by default.
|
618
|
+
#
|
421
619
|
# - Argument +source+ must be, or be convertible to, a \String:
|
422
620
|
# - If +source+ responds to instance method +to_str+,
|
423
621
|
# <tt>source.to_str</tt> becomes the source.
|
@@ -432,9 +630,6 @@ module JSON
|
|
432
630
|
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
433
631
|
# It will be called recursively with each result (depth-first order).
|
434
632
|
# See details below.
|
435
|
-
# BEWARE: This method is meant to serialise data from trusted user input,
|
436
|
-
# like from your own database server or clients under your control, it could
|
437
|
-
# be dangerous to allow untrusted users to pass JSON sources into it.
|
438
633
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
439
634
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
440
635
|
# The default options can be changed via method JSON.load_default_options=.
|
@@ -445,17 +640,17 @@ module JSON
|
|
445
640
|
# <tt>parse(source, opts)</tt>; see #parse.
|
446
641
|
#
|
447
642
|
# Source for following examples:
|
448
|
-
# source =
|
449
|
-
#
|
450
|
-
#
|
451
|
-
#
|
452
|
-
#
|
453
|
-
#
|
454
|
-
#
|
455
|
-
#
|
456
|
-
#
|
457
|
-
#
|
458
|
-
#
|
643
|
+
# source = <<~JSON
|
644
|
+
# {
|
645
|
+
# "name": "Dave",
|
646
|
+
# "age" :40,
|
647
|
+
# "hats": [
|
648
|
+
# "Cattleman's",
|
649
|
+
# "Panama",
|
650
|
+
# "Tophat"
|
651
|
+
# ]
|
652
|
+
# }
|
653
|
+
# JSON
|
459
654
|
#
|
460
655
|
# Load a \String:
|
461
656
|
# ruby = JSON.load(source)
|
@@ -647,18 +842,11 @@ module JSON
|
|
647
842
|
opts = opts.merge(:max_nesting => limit) if limit
|
648
843
|
opts = merge_dump_options(opts, **kwargs) if kwargs
|
649
844
|
|
650
|
-
|
651
|
-
generate(obj, opts)
|
845
|
+
begin
|
846
|
+
State.generate(obj, opts, anIO)
|
652
847
|
rescue JSON::NestingError
|
653
848
|
raise ArgumentError, "exceed depth limit"
|
654
849
|
end
|
655
|
-
|
656
|
-
if anIO.nil?
|
657
|
-
result
|
658
|
-
else
|
659
|
-
anIO.write result
|
660
|
-
anIO
|
661
|
-
end
|
662
850
|
end
|
663
851
|
|
664
852
|
# Encodes string using String.encode.
|
@@ -674,6 +862,82 @@ module JSON
|
|
674
862
|
class << self
|
675
863
|
private :merge_dump_options
|
676
864
|
end
|
865
|
+
|
866
|
+
# JSON::Coder holds a parser and generator configuration.
|
867
|
+
#
|
868
|
+
# module MyApp
|
869
|
+
# JSONC_CODER = JSON::Coder.new(
|
870
|
+
# allow_trailing_comma: true
|
871
|
+
# )
|
872
|
+
# end
|
873
|
+
#
|
874
|
+
# MyApp::JSONC_CODER.load(document)
|
875
|
+
#
|
876
|
+
class Coder
|
877
|
+
# :call-seq:
|
878
|
+
# JSON.new(options = nil, &block)
|
879
|
+
#
|
880
|
+
# Argument +options+, if given, contains a \Hash of options for both parsing and generating.
|
881
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
|
882
|
+
#
|
883
|
+
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
884
|
+
# encoutered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
885
|
+
# \JSON counterpart:
|
886
|
+
#
|
887
|
+
# module MyApp
|
888
|
+
# API_JSON_CODER = JSON::Coder.new do |object|
|
889
|
+
# case object
|
890
|
+
# when Time
|
891
|
+
# object.iso8601(3)
|
892
|
+
# else
|
893
|
+
# object # Unknown type, will raise
|
894
|
+
# end
|
895
|
+
# end
|
896
|
+
# end
|
897
|
+
#
|
898
|
+
# puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
899
|
+
#
|
900
|
+
def initialize(options = nil, &as_json)
|
901
|
+
if options.nil?
|
902
|
+
options = { strict: true }
|
903
|
+
else
|
904
|
+
options = options.dup
|
905
|
+
options[:strict] = true
|
906
|
+
end
|
907
|
+
options[:as_json] = as_json if as_json
|
908
|
+
options[:create_additions] = false unless options.key?(:create_additions)
|
909
|
+
|
910
|
+
@state = State.new(options).freeze
|
911
|
+
@parser_config = Ext::Parser::Config.new(options)
|
912
|
+
end
|
913
|
+
|
914
|
+
# call-seq:
|
915
|
+
# dump(object) -> String
|
916
|
+
# dump(object, io) -> io
|
917
|
+
#
|
918
|
+
# Serialize the given object into a \JSON document.
|
919
|
+
def dump(object, io = nil)
|
920
|
+
@state.generate_new(object, io)
|
921
|
+
end
|
922
|
+
alias_method :generate, :dump
|
923
|
+
|
924
|
+
# call-seq:
|
925
|
+
# load(string) -> Object
|
926
|
+
#
|
927
|
+
# Parse the given \JSON document and return an equivalent Ruby object.
|
928
|
+
def load(source)
|
929
|
+
@parser_config.parse(source)
|
930
|
+
end
|
931
|
+
alias_method :parse, :load
|
932
|
+
|
933
|
+
# call-seq:
|
934
|
+
# load(path) -> Object
|
935
|
+
#
|
936
|
+
# Parse the given \JSON document and return an equivalent Ruby object.
|
937
|
+
def load_file(path)
|
938
|
+
load(File.read(path, encoding: Encoding::UTF_8))
|
939
|
+
end
|
940
|
+
end
|
677
941
|
end
|
678
942
|
|
679
943
|
module ::Kernel
|
@@ -42,37 +42,7 @@ module JSON
|
|
42
42
|
raise TypeError, "can't convert #{opts.class} into Hash"
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
46
|
-
opts.each do |key, value|
|
47
|
-
case key
|
48
|
-
when :indent
|
49
|
-
self.indent = value
|
50
|
-
when :space
|
51
|
-
self.space = value
|
52
|
-
when :space_before
|
53
|
-
self.space_before = value
|
54
|
-
when :array_nl
|
55
|
-
self.array_nl = value
|
56
|
-
when :object_nl
|
57
|
-
self.object_nl = value
|
58
|
-
when :max_nesting
|
59
|
-
self.max_nesting = value || 0
|
60
|
-
when :depth
|
61
|
-
self.depth = value
|
62
|
-
when :buffer_initial_length
|
63
|
-
self.buffer_initial_length = value
|
64
|
-
when :allow_nan
|
65
|
-
self.allow_nan = value
|
66
|
-
when :ascii_only
|
67
|
-
self.ascii_only = value
|
68
|
-
when :script_safe, :escape_slash
|
69
|
-
self.script_safe = value
|
70
|
-
when :strict
|
71
|
-
self.strict = value
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
self
|
45
|
+
_configure(opts)
|
76
46
|
end
|
77
47
|
|
78
48
|
alias_method :merge, :configure
|
@@ -88,6 +58,7 @@ module JSON
|
|
88
58
|
space_before: space_before,
|
89
59
|
object_nl: object_nl,
|
90
60
|
array_nl: array_nl,
|
61
|
+
as_json: as_json,
|
91
62
|
allow_nan: allow_nan?,
|
92
63
|
ascii_only: ascii_only?,
|
93
64
|
max_nesting: max_nesting,
|