json 1.0.0 → 2.7.2

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 (109) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +503 -0
  3. data/LICENSE +56 -0
  4. data/README.md +416 -0
  5. data/ext/json/ext/fbuffer/fbuffer.h +187 -0
  6. data/ext/json/ext/generator/depend +1 -0
  7. data/ext/json/ext/generator/extconf.rb +2 -7
  8. data/ext/json/ext/generator/generator.c +1312 -338
  9. data/ext/json/ext/generator/generator.h +177 -0
  10. data/ext/json/ext/parser/depend +1 -0
  11. data/ext/json/ext/parser/extconf.rb +28 -5
  12. data/ext/json/ext/parser/parser.c +1349 -689
  13. data/ext/json/ext/parser/parser.h +96 -0
  14. data/ext/json/ext/parser/parser.rl +644 -188
  15. data/ext/json/extconf.rb +3 -0
  16. data/json.gemspec +68 -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 +12 -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/struct.rb +52 -0
  29. data/lib/json/add/symbol.rb +48 -0
  30. data/lib/json/add/time.rb +59 -0
  31. data/lib/json/common.rb +588 -74
  32. data/lib/json/ext.rb +3 -1
  33. data/lib/json/generic_object.rb +75 -0
  34. data/lib/json/pure/generator.rb +311 -119
  35. data/lib/json/pure/parser.rb +182 -55
  36. data/lib/json/pure.rb +5 -65
  37. data/lib/json/version.rb +2 -1
  38. data/lib/json.rb +583 -196
  39. metadata +78 -137
  40. data/CHANGES +0 -25
  41. data/GPL +0 -340
  42. data/README +0 -77
  43. data/Rakefile +0 -250
  44. data/TODO +0 -1
  45. data/VERSION +0 -1
  46. data/benchmarks/benchmark.txt +0 -133
  47. data/benchmarks/benchmark_generator.rb +0 -44
  48. data/benchmarks/benchmark_parser.rb +0 -22
  49. data/benchmarks/benchmark_rails.rb +0 -26
  50. data/bin/edit_json.rb +0 -11
  51. data/data/example.json +0 -1
  52. data/data/index.html +0 -37
  53. data/data/prototype.js +0 -2515
  54. data/ext/json/ext/generator/Makefile +0 -149
  55. data/ext/json/ext/generator/unicode.c +0 -184
  56. data/ext/json/ext/generator/unicode.h +0 -40
  57. data/ext/json/ext/parser/Makefile +0 -149
  58. data/ext/json/ext/parser/unicode.c +0 -156
  59. data/ext/json/ext/parser/unicode.h +0 -44
  60. data/install.rb +0 -26
  61. data/lib/json/Array.xpm +0 -21
  62. data/lib/json/FalseClass.xpm +0 -21
  63. data/lib/json/Hash.xpm +0 -21
  64. data/lib/json/Key.xpm +0 -73
  65. data/lib/json/NilClass.xpm +0 -21
  66. data/lib/json/Numeric.xpm +0 -28
  67. data/lib/json/String.xpm +0 -96
  68. data/lib/json/TrueClass.xpm +0 -21
  69. data/lib/json/editor.rb +0 -1207
  70. data/lib/json/json.xpm +0 -1499
  71. data/tests/fixtures/fail1.json +0 -1
  72. data/tests/fixtures/fail10.json +0 -1
  73. data/tests/fixtures/fail11.json +0 -1
  74. data/tests/fixtures/fail12.json +0 -1
  75. data/tests/fixtures/fail13.json +0 -1
  76. data/tests/fixtures/fail14.json +0 -1
  77. data/tests/fixtures/fail15.json +0 -1
  78. data/tests/fixtures/fail16.json +0 -1
  79. data/tests/fixtures/fail17.json +0 -1
  80. data/tests/fixtures/fail19.json +0 -1
  81. data/tests/fixtures/fail2.json +0 -1
  82. data/tests/fixtures/fail20.json +0 -1
  83. data/tests/fixtures/fail21.json +0 -1
  84. data/tests/fixtures/fail22.json +0 -1
  85. data/tests/fixtures/fail23.json +0 -1
  86. data/tests/fixtures/fail24.json +0 -1
  87. data/tests/fixtures/fail25.json +0 -1
  88. data/tests/fixtures/fail26.json +0 -1
  89. data/tests/fixtures/fail27.json +0 -2
  90. data/tests/fixtures/fail28.json +0 -2
  91. data/tests/fixtures/fail3.json +0 -1
  92. data/tests/fixtures/fail4.json +0 -1
  93. data/tests/fixtures/fail5.json +0 -1
  94. data/tests/fixtures/fail6.json +0 -1
  95. data/tests/fixtures/fail7.json +0 -1
  96. data/tests/fixtures/fail8.json +0 -1
  97. data/tests/fixtures/fail9.json +0 -1
  98. data/tests/fixtures/pass1.json +0 -56
  99. data/tests/fixtures/pass18.json +0 -1
  100. data/tests/fixtures/pass2.json +0 -1
  101. data/tests/fixtures/pass3.json +0 -6
  102. data/tests/runner.rb +0 -24
  103. data/tests/test_json.rb +0 -235
  104. data/tests/test_json_addition.rb +0 -94
  105. data/tests/test_json_fixtures.rb +0 -30
  106. data/tests/test_json_generate.rb +0 -81
  107. data/tests/test_json_unicode.rb +0 -55
  108. data/tools/fuzz.rb +0 -133
  109. data/tools/server.rb +0 -62
data/lib/json/common.rb CHANGED
@@ -1,45 +1,66 @@
1
+ #frozen_string_literal: false
1
2
  require 'json/version'
2
3
 
3
4
  module JSON
5
+ autoload :GenericObject, 'json/generic_object'
6
+
7
+ NOT_SET = Object.new.freeze
8
+ private_constant :NOT_SET
9
+
4
10
  class << self
5
- # If object is string like parse the string and return the parsed result as a
6
- # Ruby data structure. Otherwise generate a JSON text from the Ruby data
7
- # structure object and return it.
8
- def [](object)
11
+ # :call-seq:
12
+ # JSON[object] -> new_array or new_string
13
+ #
14
+ # If +object+ is a \String,
15
+ # calls JSON.parse with +object+ and +opts+ (see method #parse):
16
+ # json = '[0, 1, null]'
17
+ # JSON[json]# => [0, 1, nil]
18
+ #
19
+ # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
20
+ # ruby = [0, 1, nil]
21
+ # JSON[ruby] # => '[0,1,null]'
22
+ def [](object, opts = {})
9
23
  if object.respond_to? :to_str
10
- JSON.parse(object.to_str)
24
+ JSON.parse(object.to_str, opts)
11
25
  else
12
- JSON.generate(object)
26
+ JSON.generate(object, opts)
13
27
  end
14
28
  end
15
29
 
16
- # Returns the JSON parser class, that is used by JSON. This might be either
17
- # JSON::Ext::Parser or JSON::Pure::Parser.
30
+ # Returns the JSON parser class that is used by JSON. This is either
31
+ # JSON::Ext::Parser or JSON::Pure::Parser:
32
+ # JSON.parser # => JSON::Ext::Parser
18
33
  attr_reader :parser
19
34
 
20
35
  # Set the JSON parser class _parser_ to be used by JSON.
21
36
  def parser=(parser) # :nodoc:
22
37
  @parser = parser
23
- remove_const :Parser if const_defined? :Parser
38
+ remove_const :Parser if const_defined?(:Parser, false)
24
39
  const_set :Parser, parser
25
40
  end
26
41
 
27
42
  # Return the constant located at _path_. The format of _path_ has to be
28
- # either ::A::B::C or A::B::C. In any case A has to be located at the top
43
+ # either ::A::B::C or A::B::C. In any case, A has to be located at the top
29
44
  # level (absolute namespace path?). If there doesn't exist a constant at
30
45
  # the given path, an ArgumentError is raised.
31
46
  def deep_const_get(path) # :nodoc:
32
- path.split(/::/).inject(Object) do |p, c|
47
+ path.to_s.split(/::/).inject(Object) do |p, c|
33
48
  case
34
- when c.empty? then p
35
- when p.const_defined?(c) then p.const_get(c)
36
- else raise ArgumentError, "can't find #{path}"
49
+ when c.empty? then p
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
37
57
  end
38
58
  end
39
59
  end
40
60
 
41
61
  # Set the module _generator_ to be used by JSON.
42
62
  def generator=(generator) # :nodoc:
63
+ old, $VERBOSE = $VERBOSE, nil
43
64
  @generator = generator
44
65
  generator_methods = generator::GeneratorMethods
45
66
  for const in generator_methods.constants
@@ -54,131 +75,624 @@ module JSON
54
75
  end
55
76
  self.state = generator::State
56
77
  const_set :State, self.state
78
+ const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
79
+ const_set :FAST_STATE_PROTOTYPE, create_fast_state
80
+ const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state
81
+ ensure
82
+ $VERBOSE = old
83
+ end
84
+
85
+ def create_fast_state
86
+ State.new(
87
+ :indent => '',
88
+ :space => '',
89
+ :object_nl => "",
90
+ :array_nl => "",
91
+ :max_nesting => false
92
+ )
93
+ end
94
+
95
+ def create_pretty_state
96
+ State.new(
97
+ :indent => ' ',
98
+ :space => ' ',
99
+ :object_nl => "\n",
100
+ :array_nl => "\n"
101
+ )
57
102
  end
58
103
 
59
- # Returns the JSON generator modul, that is used by JSON. This might be
60
- # either JSON::Ext::Generator or JSON::Pure::Generator.
104
+ # Returns the JSON generator module that is used by JSON. This is
105
+ # either JSON::Ext::Generator or JSON::Pure::Generator:
106
+ # JSON.generator # => JSON::Ext::Generator
61
107
  attr_reader :generator
62
108
 
63
- # Returns the JSON generator state class, that is used by JSON. This might
64
- # be either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
109
+ # Sets or Returns the JSON generator state class that is used by JSON. This is
110
+ # either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
111
+ # JSON.state # => JSON::Ext::Generator::State
65
112
  attr_accessor :state
113
+ end
66
114
 
67
- # This is create identifier, that is used to decide, if the _json_create_
68
- # hook of a class should be called. It defaults to 'json_class'.
69
- attr_accessor :create_id
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
+ # Sets create identifier, which is used to decide if the _json_create_
122
+ # hook of a class should be called; initial value is +json_class+:
123
+ # JSON.create_id # => 'json_class'
124
+ def self.create_id=(new_value)
125
+ Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
126
+ end
127
+
128
+ # Returns the current create identifier.
129
+ # See also JSON.create_id=.
130
+ def self.create_id
131
+ Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
70
132
  end
71
- self.create_id = 'json_class'
133
+
134
+ NaN = 0.0/0
135
+
136
+ Infinity = 1.0/0
137
+
138
+ MinusInfinity = -Infinity
72
139
 
73
140
  # The base exception for JSON errors.
74
- class JSONError < StandardError; end
141
+ class JSONError < StandardError
142
+ def self.wrap(exception)
143
+ obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
144
+ obj.set_backtrace exception.backtrace
145
+ obj
146
+ end
147
+ end
75
148
 
76
- # This exception is raised, if a parser error occurs.
149
+ # This exception is raised if a parser error occurs.
77
150
  class ParserError < JSONError; end
78
151
 
79
- # This exception is raised, if a generator or unparser error occurs.
152
+ # This exception is raised if the nesting of parsed data structures is too
153
+ # deep.
154
+ class NestingError < ParserError; end
155
+
156
+ # :stopdoc:
157
+ class CircularDatastructure < NestingError; end
158
+ # :startdoc:
159
+
160
+ # This exception is raised if a generator or unparser error occurs.
80
161
  class GeneratorError < JSONError; end
81
162
  # For backwards compatibility
82
- UnparserError = GeneratorError
163
+ UnparserError = GeneratorError # :nodoc:
83
164
 
84
- # If a circular data structure is encountered while unparsing
85
- # this exception is raised.
86
- class CircularDatastructure < GeneratorError; end
87
-
88
- # This exception is raised, if the required unicode support is missing on the
89
- # system. Usually this means, that the iconv library is not installed.
165
+ # This exception is raised if the required unicode support is missing on the
166
+ # system. Usually this means that the iconv library is not installed.
90
167
  class MissingUnicodeSupport < JSONError; end
91
168
 
92
169
  module_function
93
170
 
94
- # Parse the JSON string _source_ into a Ruby data structure and return it.
95
- def parse(source)
96
- JSON.parser.new(source).parse
171
+ # :call-seq:
172
+ # JSON.parse(source, opts) -> object
173
+ #
174
+ # Returns the Ruby objects created by parsing the given +source+.
175
+ #
176
+ # Argument +source+ contains the \String to be parsed.
177
+ #
178
+ # Argument +opts+, if given, contains a \Hash of options for the parsing.
179
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
180
+ #
181
+ # ---
182
+ #
183
+ # When +source+ is a \JSON array, returns a Ruby \Array:
184
+ # source = '["foo", 1.0, true, false, null]'
185
+ # ruby = JSON.parse(source)
186
+ # ruby # => ["foo", 1.0, true, false, nil]
187
+ # ruby.class # => Array
188
+ #
189
+ # When +source+ is a \JSON object, returns a Ruby \Hash:
190
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
191
+ # ruby = JSON.parse(source)
192
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
193
+ # ruby.class # => Hash
194
+ #
195
+ # For examples of parsing for all \JSON data types, see
196
+ # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
197
+ #
198
+ # Parses nested JSON objects:
199
+ # source = <<-EOT
200
+ # {
201
+ # "name": "Dave",
202
+ # "age" :40,
203
+ # "hats": [
204
+ # "Cattleman's",
205
+ # "Panama",
206
+ # "Tophat"
207
+ # ]
208
+ # }
209
+ # EOT
210
+ # ruby = JSON.parse(source)
211
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
212
+ #
213
+ # ---
214
+ #
215
+ # Raises an exception if +source+ is not valid JSON:
216
+ # # Raises JSON::ParserError (783: unexpected token at ''):
217
+ # JSON.parse('')
218
+ #
219
+ def parse(source, opts = {})
220
+ Parser.new(source, **(opts||{})).parse
221
+ end
222
+
223
+ # :call-seq:
224
+ # JSON.parse!(source, opts) -> object
225
+ #
226
+ # Calls
227
+ # parse(source, opts)
228
+ # with +source+ and possibly modified +opts+.
229
+ #
230
+ # Differences from JSON.parse:
231
+ # - Option +max_nesting+, if not provided, defaults to +false+,
232
+ # which disables checking for nesting depth.
233
+ # - Option +allow_nan+, if not provided, defaults to +true+.
234
+ def parse!(source, opts = {})
235
+ opts = {
236
+ :max_nesting => false,
237
+ :allow_nan => true
238
+ }.merge(opts)
239
+ Parser.new(source, **(opts||{})).parse
97
240
  end
98
241
 
99
- # Unparse the Ruby data structure _obj_ into a single line JSON string and
100
- # return it. _state_ is a JSON::State object, that can be used to configure
101
- # the output further.
242
+ # :call-seq:
243
+ # JSON.load_file(path, opts={}) -> object
244
+ #
245
+ # Calls:
246
+ # parse(File.read(path), opts)
102
247
  #
103
- # It defaults to a state object, that creates the shortest possible JSON text
104
- # in one line and only checks for circular data structures. If you are sure,
105
- # that the objects don't contain any circles, you can set _state_ to nil, to
106
- # disable these checks in order to create the JSON text faster. See also
107
- # fast_generate.
108
- def generate(obj, state = JSON.state.new)
109
- obj.to_json(state)
248
+ # See method #parse.
249
+ def load_file(filespec, opts = {})
250
+ parse(File.read(filespec), opts)
110
251
  end
111
252
 
253
+ # :call-seq:
254
+ # JSON.load_file!(path, opts = {})
255
+ #
256
+ # Calls:
257
+ # JSON.parse!(File.read(path, opts))
258
+ #
259
+ # See method #parse!
260
+ def load_file!(filespec, opts = {})
261
+ parse!(File.read(filespec), opts)
262
+ end
263
+
264
+ # :call-seq:
265
+ # JSON.generate(obj, opts = nil) -> new_string
266
+ #
267
+ # Returns a \String containing the generated \JSON data.
268
+ #
269
+ # See also JSON.fast_generate, JSON.pretty_generate.
270
+ #
271
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
272
+ #
273
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
274
+ # See {Generating Options}[#module-JSON-label-Generating+Options].
275
+ #
276
+ # ---
277
+ #
278
+ # When +obj+ is an \Array, returns a \String containing a \JSON array:
279
+ # obj = ["foo", 1.0, true, false, nil]
280
+ # json = JSON.generate(obj)
281
+ # json # => '["foo",1.0,true,false,null]'
282
+ #
283
+ # When +obj+ is a \Hash, returns a \String containing a \JSON object:
284
+ # obj = {foo: 0, bar: 's', baz: :bat}
285
+ # json = JSON.generate(obj)
286
+ # json # => '{"foo":0,"bar":"s","baz":"bat"}'
287
+ #
288
+ # For examples of generating from other Ruby objects, see
289
+ # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
290
+ #
291
+ # ---
292
+ #
293
+ # Raises an exception if any formatting option is not a \String.
294
+ #
295
+ # Raises an exception if +obj+ contains circular references:
296
+ # a = []; b = []; a.push(b); b.push(a)
297
+ # # Raises JSON::NestingError (nesting of 100 is too deep):
298
+ # JSON.generate(a)
299
+ #
300
+ def generate(obj, opts = nil)
301
+ if State === opts
302
+ state = opts
303
+ else
304
+ state = State.new(opts)
305
+ end
306
+ state.generate(obj)
307
+ end
308
+
309
+ # :stopdoc:
310
+ # I want to deprecate these later, so I'll first be silent about them, and
311
+ # later delete them.
112
312
  alias unparse generate
113
313
  module_function :unparse
314
+ # :startdoc:
114
315
 
115
- # Unparse the Ruby data structure _obj_ into a single line JSON string and
116
- # return it. This method disables the checks for circles in Ruby objects.
316
+ # :call-seq:
317
+ # JSON.fast_generate(obj, opts) -> new_string
117
318
  #
118
- # *WARNING*: Be careful not to pass any Ruby data structures with circles as
119
- # _obj_ argument, because this will cause JSON to go into an infinite loop.
120
- def fast_generate(obj)
121
- obj.to_json(nil)
319
+ # Arguments +obj+ and +opts+ here are the same as
320
+ # arguments +obj+ and +opts+ in JSON.generate.
321
+ #
322
+ # By default, generates \JSON data without checking
323
+ # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
324
+ #
325
+ # Raises an exception if +obj+ contains circular references:
326
+ # a = []; b = []; a.push(b); b.push(a)
327
+ # # Raises SystemStackError (stack level too deep):
328
+ # JSON.fast_generate(a)
329
+ def fast_generate(obj, opts = nil)
330
+ if State === opts
331
+ state = opts
332
+ else
333
+ state = JSON.create_fast_state.configure(opts)
334
+ end
335
+ state.generate(obj)
122
336
  end
123
337
 
338
+ # :stopdoc:
339
+ # I want to deprecate these later, so I'll first be silent about them, and later delete them.
124
340
  alias fast_unparse fast_generate
125
341
  module_function :fast_unparse
342
+ # :startdoc:
126
343
 
127
- # Unparse the Ruby data structure _obj_ into a JSON string and return it. The
128
- # returned string is a prettier form of the string returned by #unparse.
129
- def pretty_generate(obj)
130
- state = JSON.state.new(
131
- :indent => ' ',
132
- :space => ' ',
133
- :object_nl => "\n",
134
- :array_nl => "\n",
135
- :check_circular => true
136
- )
137
- obj.to_json(state)
344
+ # :call-seq:
345
+ # JSON.pretty_generate(obj, opts = nil) -> new_string
346
+ #
347
+ # Arguments +obj+ and +opts+ here are the same as
348
+ # arguments +obj+ and +opts+ in JSON.generate.
349
+ #
350
+ # Default options are:
351
+ # {
352
+ # indent: ' ', # Two spaces
353
+ # space: ' ', # One space
354
+ # array_nl: "\n", # Newline
355
+ # object_nl: "\n" # Newline
356
+ # }
357
+ #
358
+ # Example:
359
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
360
+ # json = JSON.pretty_generate(obj)
361
+ # puts json
362
+ # Output:
363
+ # {
364
+ # "foo": [
365
+ # "bar",
366
+ # "baz"
367
+ # ],
368
+ # "bat": {
369
+ # "bam": 0,
370
+ # "bad": 1
371
+ # }
372
+ # }
373
+ #
374
+ def pretty_generate(obj, opts = nil)
375
+ if State === opts
376
+ state, opts = opts, nil
377
+ else
378
+ state = JSON.create_pretty_state
379
+ end
380
+ if opts
381
+ if opts.respond_to? :to_hash
382
+ opts = opts.to_hash
383
+ elsif opts.respond_to? :to_h
384
+ opts = opts.to_h
385
+ else
386
+ raise TypeError, "can't convert #{opts.class} into Hash"
387
+ end
388
+ state.configure(opts)
389
+ end
390
+ state.generate(obj)
138
391
  end
139
392
 
393
+ # :stopdoc:
394
+ # I want to deprecate these later, so I'll first be silent about them, and later delete them.
140
395
  alias pretty_unparse pretty_generate
141
396
  module_function :pretty_unparse
397
+ # :startdoc:
398
+
399
+ class << self
400
+ # Sets or returns default options for the JSON.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 :load_default_options
405
+ end
406
+ self.load_default_options = {
407
+ :max_nesting => false,
408
+ :allow_nan => true,
409
+ :allow_blank => true,
410
+ :create_additions => true,
411
+ }
412
+
413
+ # :call-seq:
414
+ # JSON.load(source, proc = nil, options = {}) -> object
415
+ #
416
+ # Returns the Ruby objects created by parsing the given +source+.
417
+ #
418
+ # - Argument +source+ must be, or be convertible to, a \String:
419
+ # - If +source+ responds to instance method +to_str+,
420
+ # <tt>source.to_str</tt> becomes the source.
421
+ # - If +source+ responds to instance method +to_io+,
422
+ # <tt>source.to_io.read</tt> becomes the source.
423
+ # - If +source+ responds to instance method +read+,
424
+ # <tt>source.read</tt> becomes the source.
425
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
426
+ # - Option +allow_blank+ specifies a truthy value.
427
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
428
+ # - Otherwise, +source+ remains the source.
429
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
430
+ # It will be called recursively with each result (depth-first order).
431
+ # 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
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
436
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
437
+ # The default options can be changed via method JSON.load_default_options=.
438
+ #
439
+ # ---
440
+ #
441
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
442
+ # <tt>parse(source, opts)</tt>; see #parse.
443
+ #
444
+ # Source for following examples:
445
+ # source = <<-EOT
446
+ # {
447
+ # "name": "Dave",
448
+ # "age" :40,
449
+ # "hats": [
450
+ # "Cattleman's",
451
+ # "Panama",
452
+ # "Tophat"
453
+ # ]
454
+ # }
455
+ # EOT
456
+ #
457
+ # Load a \String:
458
+ # ruby = JSON.load(source)
459
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
460
+ #
461
+ # Load an \IO object:
462
+ # require 'stringio'
463
+ # object = JSON.load(StringIO.new(source))
464
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
465
+ #
466
+ # Load a \File object:
467
+ # path = 't.json'
468
+ # File.write(path, source)
469
+ # File.open(path) do |file|
470
+ # JSON.load(file)
471
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
472
+ #
473
+ # ---
474
+ #
475
+ # When +proc+ is given:
476
+ # - Modifies +source+ as above.
477
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
478
+ # - Recursively calls <tt>proc(result)</tt>.
479
+ # - Returns the final result.
480
+ #
481
+ # Example:
482
+ # require 'json'
483
+ #
484
+ # # Some classes for the example.
485
+ # class Base
486
+ # def initialize(attributes)
487
+ # @attributes = attributes
488
+ # end
489
+ # end
490
+ # class User < Base; end
491
+ # class Account < Base; end
492
+ # class Admin < Base; end
493
+ # # The JSON source.
494
+ # json = <<-EOF
495
+ # {
496
+ # "users": [
497
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
498
+ # {"type": "User", "username": "john", "email": "john@example.com"}
499
+ # ],
500
+ # "accounts": [
501
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
502
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
503
+ # ],
504
+ # "admins": {"type": "Admin", "password": "0wn3d"}
505
+ # }
506
+ # EOF
507
+ # # Deserializer method.
508
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
509
+ # type = obj.is_a?(Hash) && obj["type"]
510
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
511
+ # end
512
+ # # Call to JSON.load
513
+ # ruby = JSON.load(json, proc {|obj|
514
+ # case obj
515
+ # when Hash
516
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
517
+ # when Array
518
+ # obj.map! {|v| deserialize_obj v }
519
+ # end
520
+ # })
521
+ # pp ruby
522
+ # Output:
523
+ # {"users"=>
524
+ # [#<User:0x00000000064c4c98
525
+ # @attributes=
526
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
527
+ # #<User:0x00000000064c4bd0
528
+ # @attributes=
529
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
530
+ # "accounts"=>
531
+ # [{"account"=>
532
+ # #<Account:0x00000000064c4928
533
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
534
+ # {"account"=>
535
+ # #<Account:0x00000000064c4680
536
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
537
+ # "admins"=>
538
+ # #<Admin:0x00000000064c41f8
539
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
540
+ #
541
+ def load(source, proc = nil, options = {})
542
+ opts = load_default_options.merge options
543
+ if source.respond_to? :to_str
544
+ source = source.to_str
545
+ elsif source.respond_to? :to_io
546
+ source = source.to_io.read
547
+ elsif source.respond_to?(:read)
548
+ source = source.read
549
+ end
550
+ if opts[:allow_blank] && (source.nil? || source.empty?)
551
+ source = 'null'
552
+ end
553
+ result = parse(source, opts)
554
+ recurse_proc(result, &proc) if proc
555
+ result
556
+ end
557
+
558
+ # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
559
+ def recurse_proc(result, &proc) # :nodoc:
560
+ case result
561
+ when Array
562
+ result.each { |x| recurse_proc x, &proc }
563
+ proc.call result
564
+ when Hash
565
+ result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
566
+ proc.call result
567
+ else
568
+ proc.call result
569
+ end
570
+ end
571
+
572
+ alias restore load
573
+ module_function :restore
574
+
575
+ class << self
576
+ # Sets or returns the default options for the JSON.dump method.
577
+ # Initially:
578
+ # opts = JSON.dump_default_options
579
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :script_safe=>false}
580
+ attr_accessor :dump_default_options
581
+ end
582
+ self.dump_default_options = {
583
+ :max_nesting => false,
584
+ :allow_nan => true,
585
+ :script_safe => false,
586
+ }
587
+
588
+ # :call-seq:
589
+ # JSON.dump(obj, io = nil, limit = nil)
590
+ #
591
+ # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
592
+ #
593
+ # The default options can be changed via method JSON.dump_default_options.
594
+ #
595
+ # - Argument +io+, if given, should respond to method +write+;
596
+ # the \JSON \String is written to +io+, and +io+ is returned.
597
+ # If +io+ is not given, the \JSON \String is returned.
598
+ # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
599
+ #
600
+ # ---
601
+ #
602
+ # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
603
+ # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
604
+ # json = JSON.dump(obj)
605
+ # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
606
+ #
607
+ # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
608
+ # path = 't.json'
609
+ # File.open(path, 'w') do |file|
610
+ # JSON.dump(obj, file)
611
+ # end # => #<File:t.json (closed)>
612
+ # puts File.read(path)
613
+ # Output:
614
+ # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
615
+ def dump(obj, anIO = nil, limit = nil, kwargs = nil)
616
+ io_limit_opt = [anIO, limit, kwargs].compact
617
+ kwargs = io_limit_opt.pop if io_limit_opt.last.is_a?(Hash)
618
+ anIO, limit = io_limit_opt
619
+ if anIO.respond_to?(:to_io)
620
+ anIO = anIO.to_io
621
+ elsif limit.nil? && !anIO.respond_to?(:write)
622
+ anIO, limit = nil, anIO
623
+ end
624
+ opts = JSON.dump_default_options
625
+ opts = opts.merge(:max_nesting => limit) if limit
626
+ opts = merge_dump_options(opts, **kwargs) if kwargs
627
+ result = generate(obj, opts)
628
+ if anIO
629
+ anIO.write result
630
+ anIO
631
+ else
632
+ result
633
+ end
634
+ rescue JSON::NestingError
635
+ raise ArgumentError, "exceed depth limit"
636
+ end
637
+
638
+ # Encodes string using String.encode.
639
+ def self.iconv(to, from, string)
640
+ string.encode(to, from)
641
+ end
642
+
643
+ def merge_dump_options(opts, strict: NOT_SET)
644
+ opts = opts.merge(strict: strict) if NOT_SET != strict
645
+ opts
646
+ end
647
+
648
+ class << self
649
+ private :merge_dump_options
650
+ end
142
651
  end
143
652
 
144
653
  module ::Kernel
654
+ private
655
+
145
656
  # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
146
657
  # one line.
147
658
  def j(*objs)
148
659
  objs.each do |obj|
149
- puts JSON::generate(obj)
660
+ puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
150
661
  end
151
662
  nil
152
663
  end
153
664
 
154
- # Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
665
+ # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
155
666
  # indentation and over many lines.
156
667
  def jj(*objs)
157
668
  objs.each do |obj|
158
- puts JSON::pretty_generate(obj)
669
+ puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
159
670
  end
160
671
  nil
161
672
  end
162
673
 
163
- # If object is string like parse the string and return the parsed result as a
164
- # Ruby data structure. Otherwise generate a JSON text from the Ruby data
674
+ # If _object_ is string-like, parse the string and return the parsed result as
675
+ # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
165
676
  # structure object and return it.
166
- def JSON(object)
677
+ #
678
+ # The _opts_ argument is passed through to generate/parse respectively. See
679
+ # generate and parse for their documentation.
680
+ def JSON(object, *args)
167
681
  if object.respond_to? :to_str
168
- JSON.parse(object.to_str)
682
+ JSON.parse(object.to_str, args.first)
169
683
  else
170
- JSON.generate(object)
684
+ JSON.generate(object, args.first)
171
685
  end
172
686
  end
173
687
  end
174
688
 
689
+ # Extends any Class to include _json_creatable?_ method.
175
690
  class ::Class
176
- # Returns true, if this class can be used to create an instance
691
+ # Returns true if this class can be used to create an instance
177
692
  # from a serialised JSON string. The class has to implement a class
178
- # method _json_create_ that expects a hash as first parameter, which includes
179
- # the required data.
693
+ # method _json_create_ that expects a hash as first parameter. The hash
694
+ # should include the required data.
180
695
  def json_creatable?
181
696
  respond_to?(:json_create)
182
697
  end
183
698
  end
184
- # vim: set et sw=2 ts=2: