json 1.0.0 → 2.7.2

Sign up to get free protection for your applications and to get access to all the features.
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: