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/ext.rb CHANGED
@@ -6,8 +6,10 @@ module JSON
6
6
  module Ext
7
7
  require 'json/ext/parser'
8
8
  require 'json/ext/generator'
9
- $DEBUG and warn "Using c extension for JSON."
9
+ $DEBUG and warn "Using Ext extension for JSON."
10
10
  JSON.parser = Parser
11
11
  JSON.generator = Generator
12
12
  end
13
+
14
+ JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
13
15
  end
@@ -0,0 +1,75 @@
1
+ #frozen_string_literal: false
2
+ begin
3
+ require 'ostruct'
4
+ rescue LoadError
5
+ warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
6
+ end
7
+
8
+ module JSON
9
+ class GenericObject < OpenStruct
10
+ class << self
11
+ alias [] new
12
+
13
+ def json_creatable?
14
+ @json_creatable
15
+ end
16
+
17
+ attr_writer :json_creatable
18
+
19
+ def json_create(data)
20
+ data = data.dup
21
+ data.delete JSON.create_id
22
+ self[data]
23
+ end
24
+
25
+ def from_hash(object)
26
+ case
27
+ when object.respond_to?(:to_hash)
28
+ result = new
29
+ object.to_hash.each do |key, value|
30
+ result[key] = from_hash(value)
31
+ end
32
+ result
33
+ when object.respond_to?(:to_ary)
34
+ object.to_ary.map { |a| from_hash(a) }
35
+ else
36
+ object
37
+ end
38
+ end
39
+
40
+ def load(source, proc = nil, opts = {})
41
+ result = ::JSON.load(source, proc, opts.merge(:object_class => self))
42
+ result.nil? ? new : result
43
+ end
44
+
45
+ def dump(obj, *args)
46
+ ::JSON.dump(obj, *args)
47
+ end
48
+ end
49
+ self.json_creatable = false
50
+
51
+ def to_hash
52
+ table
53
+ end
54
+
55
+ def [](name)
56
+ __send__(name)
57
+ end unless method_defined?(:[])
58
+
59
+ def []=(name, value)
60
+ __send__("#{name}=", value)
61
+ end unless method_defined?(:[]=)
62
+
63
+ def |(other)
64
+ self.class[other.to_hash.merge(to_hash)]
65
+ end
66
+
67
+ def as_json(*)
68
+ { JSON.create_id => self.class.name }.merge to_hash
69
+ end
70
+
71
+ def to_json(*a)
72
+ as_json.to_json(*a)
73
+ end
74
+ end if defined?(::OpenStruct)
75
+ end
@@ -1,3 +1,4 @@
1
+ #frozen_string_literal: false
1
2
  module JSON
2
3
  MAP = {
3
4
  "\x0" => '\u0000',
@@ -34,48 +35,83 @@ module JSON
34
35
  "\x1f" => '\u001f',
35
36
  '"' => '\"',
36
37
  '\\' => '\\\\',
37
- '/' => '\/',
38
38
  } # :nodoc:
39
39
 
40
+ ESCAPE_PATTERN = /[\/"\\\x0-\x1f]/n # :nodoc:
41
+
42
+ SCRIPT_SAFE_MAP = MAP.merge(
43
+ '/' => '\\/',
44
+ "\u2028".b => '\u2028',
45
+ "\u2029".b => '\u2029',
46
+ )
47
+
48
+ SCRIPT_SAFE_ESCAPE_PATTERN = Regexp.union(ESCAPE_PATTERN, "\u2028".b, "\u2029".b)
49
+
40
50
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
41
51
  # UTF16 big endian characters as \u????, and return it.
42
- def utf8_to_json(string) # :nodoc:
43
- string = string.gsub(/["\\\/\x0-\x1f]/) { |c| MAP[c] }
52
+ def utf8_to_json(string, script_safe = false) # :nodoc:
53
+ string = string.dup
54
+ string.force_encoding(::Encoding::ASCII_8BIT)
55
+ if script_safe
56
+ string.gsub!(SCRIPT_SAFE_ESCAPE_PATTERN) { SCRIPT_SAFE_MAP[$&] || $& }
57
+ else
58
+ string.gsub!(ESCAPE_PATTERN) { MAP[$&] || $& }
59
+ end
60
+ string.force_encoding(::Encoding::UTF_8)
61
+ string
62
+ end
63
+
64
+ def utf8_to_json_ascii(string, script_safe = false) # :nodoc:
65
+ string = string.dup
66
+ string.force_encoding(::Encoding::ASCII_8BIT)
67
+ map = script_safe ? SCRIPT_SAFE_MAP : MAP
68
+ string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
44
69
  string.gsub!(/(
45
- (?:
46
- [\xc2-\xdf][\x80-\xbf] |
47
- [\xe0-\xef][\x80-\xbf]{2} |
48
- [\xf0-\xf4][\x80-\xbf]{3}
49
- )+ |
50
- [\x80-\xc1\xf5-\xff] # invalid
51
- )/nx) { |c|
70
+ (?:
71
+ [\xc2-\xdf][\x80-\xbf] |
72
+ [\xe0-\xef][\x80-\xbf]{2} |
73
+ [\xf0-\xf4][\x80-\xbf]{3}
74
+ )+ |
75
+ [\x80-\xc1\xf5-\xff] # invalid
76
+ )/nx) { |c|
52
77
  c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
53
- s = JSON::UTF8toUTF16.iconv(c).unpack('H*')[0]
78
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
79
+ s.force_encoding(::Encoding::ASCII_8BIT)
54
80
  s.gsub!(/.{4}/n, '\\\\u\&')
81
+ s.force_encoding(::Encoding::UTF_8)
55
82
  }
83
+ string.force_encoding(::Encoding::UTF_8)
56
84
  string
57
- rescue Iconv::Failure => e
58
- raise GeneratorError, "Caught #{e.class}: #{e}"
85
+ rescue => e
86
+ raise GeneratorError.wrap(e)
59
87
  end
60
- module_function :utf8_to_json
88
+
89
+ def valid_utf8?(string)
90
+ encoding = string.encoding
91
+ (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
92
+ string.valid_encoding?
93
+ end
94
+ module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8?
61
95
 
62
96
  module Pure
63
97
  module Generator
64
98
  # This class is used to create State instances, that are use to hold data
65
- # while generating a JSON text from a a Ruby data structure.
99
+ # while generating a JSON text from a Ruby data structure.
66
100
  class State
67
101
  # Creates a State object from _opts_, which ought to be Hash to create
68
102
  # a new State instance configured by _opts_, something else to create
69
103
  # an unconfigured instance. If _opts_ is a State object, it is just
70
104
  # returned.
71
105
  def self.from_state(opts)
72
- case opts
73
- when self
106
+ case
107
+ when self === opts
74
108
  opts
75
- when Hash
76
- new(opts)
109
+ when opts.respond_to?(:to_hash)
110
+ new(opts.to_hash)
111
+ when opts.respond_to?(:to_h)
112
+ new(opts.to_h)
77
113
  else
78
- new
114
+ SAFE_STATE_PROTOTYPE.dup
79
115
  end
80
116
  end
81
117
 
@@ -86,18 +122,28 @@ module JSON
86
122
  # * *indent*: a string used to indent levels (default: ''),
87
123
  # * *space*: a string that is put after, a : or , delimiter (default: ''),
88
124
  # * *space_before*: a string that is put before a : pair delimiter (default: ''),
89
- # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
125
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
90
126
  # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
91
- # * *check_circular*: true if checking for circular data structures
92
- # should be done, false (the default) otherwise.
127
+ # * *script_safe*: true if U+2028, U+2029 and forward slash (/) should be escaped
128
+ # as to make the JSON object safe to interpolate in a script tag (default: false).
129
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
130
+ # * *max_nesting*: sets the maximum level of data structure nesting in
131
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
132
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
133
+ # generated, otherwise an exception is thrown, if these values are
134
+ # encountered. This options defaults to false.
93
135
  def initialize(opts = {})
94
- @indent = opts[:indent] || ''
95
- @space = opts[:space] || ''
96
- @space_before = opts[:space_before] || ''
97
- @object_nl = opts[:object_nl] || ''
98
- @array_nl = opts[:array_nl] || ''
99
- @check_circular = !!(opts[:check_circular] || false)
100
- @seen = {}
136
+ @indent = ''
137
+ @space = ''
138
+ @space_before = ''
139
+ @object_nl = ''
140
+ @array_nl = ''
141
+ @allow_nan = false
142
+ @ascii_only = false
143
+ @script_safe = false
144
+ @strict = false
145
+ @buffer_initial_length = 1024
146
+ configure opts
101
147
  end
102
148
 
103
149
  # This string is used to indent levels in the JSON text.
@@ -117,27 +163,151 @@ module JSON
117
163
  # This string is put at the end of a line that holds a JSON array.
118
164
  attr_accessor :array_nl
119
165
 
120
- # Returns true, if circular data structures should be checked,
166
+ # This integer returns the maximum level of data structure nesting in
167
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
168
+ attr_accessor :max_nesting
169
+
170
+ # If this attribute is set to true, forward slashes will be escaped in
171
+ # all json strings.
172
+ attr_accessor :script_safe
173
+
174
+ # If this attribute is set to true, attempting to serialize types not
175
+ # supported by the JSON spec will raise a JSON::GeneratorError
176
+ attr_accessor :strict
177
+
178
+ # :stopdoc:
179
+ attr_reader :buffer_initial_length
180
+
181
+ def buffer_initial_length=(length)
182
+ if length > 0
183
+ @buffer_initial_length = length
184
+ end
185
+ end
186
+ # :startdoc:
187
+
188
+ # This integer returns the current depth data structure nesting in the
189
+ # generated JSON.
190
+ attr_accessor :depth
191
+
192
+ def check_max_nesting # :nodoc:
193
+ return if @max_nesting.zero?
194
+ current_nesting = depth + 1
195
+ current_nesting > @max_nesting and
196
+ raise NestingError, "nesting of #{current_nesting} is too deep"
197
+ end
198
+
199
+ # Returns true, if circular data structures are checked,
121
200
  # otherwise returns false.
122
201
  def check_circular?
123
- @check_circular
202
+ !@max_nesting.zero?
124
203
  end
125
204
 
126
- # Returns _true_, if _object_ was already seen during this generating
127
- # run.
128
- def seen?(object)
129
- @seen.key?(object.__id__)
205
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
206
+ # valid JSON and output.
207
+ def allow_nan?
208
+ @allow_nan
130
209
  end
131
210
 
132
- # Remember _object_, to find out if it was already encountered (if a
133
- # cyclic data structure is if a cyclic data structure is rendered).
134
- def remember(object)
135
- @seen[object.__id__] = true
211
+ # Returns true, if only ASCII characters should be generated. Otherwise
212
+ # returns false.
213
+ def ascii_only?
214
+ @ascii_only
136
215
  end
137
216
 
138
- # Forget _object_ for this generating run.
139
- def forget(object)
140
- @seen.delete object.__id__
217
+ # Returns true, if forward slashes are escaped. Otherwise returns false.
218
+ def script_safe?
219
+ @script_safe
220
+ end
221
+
222
+ # Returns true, if forward slashes are escaped. Otherwise returns false.
223
+ def strict?
224
+ @strict
225
+ end
226
+
227
+ # Configure this State instance with the Hash _opts_, and return
228
+ # itself.
229
+ def configure(opts)
230
+ if opts.respond_to?(:to_hash)
231
+ opts = opts.to_hash
232
+ elsif opts.respond_to?(:to_h)
233
+ opts = opts.to_h
234
+ else
235
+ raise TypeError, "can't convert #{opts.class} into Hash"
236
+ end
237
+ opts.each do |key, value|
238
+ instance_variable_set "@#{key}", value
239
+ end
240
+ @indent = opts[:indent] if opts.key?(:indent)
241
+ @space = opts[:space] if opts.key?(:space)
242
+ @space_before = opts[:space_before] if opts.key?(:space_before)
243
+ @object_nl = opts[:object_nl] if opts.key?(:object_nl)
244
+ @array_nl = opts[:array_nl] if opts.key?(:array_nl)
245
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
246
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
247
+ @depth = opts[:depth] || 0
248
+ @buffer_initial_length ||= opts[:buffer_initial_length]
249
+
250
+ @script_safe = if opts.key?(:script_safe)
251
+ !!opts[:script_safe]
252
+ elsif opts.key?(:escape_slash)
253
+ !!opts[:escape_slash]
254
+ else
255
+ false
256
+ end
257
+
258
+ @strict = !!opts[:strict] if opts.key?(:strict)
259
+
260
+ if !opts.key?(:max_nesting) # defaults to 100
261
+ @max_nesting = 100
262
+ elsif opts[:max_nesting]
263
+ @max_nesting = opts[:max_nesting]
264
+ else
265
+ @max_nesting = 0
266
+ end
267
+ self
268
+ end
269
+ alias merge configure
270
+
271
+ # Returns the configuration instance variables as a hash, that can be
272
+ # passed to the configure method.
273
+ def to_h
274
+ result = {}
275
+ instance_variables.each do |iv|
276
+ iv = iv.to_s[1..-1]
277
+ result[iv.to_sym] = self[iv]
278
+ end
279
+ result
280
+ end
281
+
282
+ alias to_hash to_h
283
+
284
+ # Generates a valid JSON document from object +obj+ and
285
+ # returns the result. If no valid JSON document can be
286
+ # created this method raises a
287
+ # GeneratorError exception.
288
+ def generate(obj)
289
+ result = obj.to_json(self)
290
+ JSON.valid_utf8?(result) or raise GeneratorError,
291
+ "source sequence #{result.inspect} is illegal/malformed utf-8"
292
+ result
293
+ end
294
+
295
+ # Return the value returned by method +name+.
296
+ def [](name)
297
+ if respond_to?(name)
298
+ __send__(name)
299
+ else
300
+ instance_variable_get("@#{name}") if
301
+ instance_variables.include?("@#{name}".to_sym) # avoid warning
302
+ end
303
+ end
304
+
305
+ def []=(name, value)
306
+ if respond_to?(name_writer = "#{name}=")
307
+ __send__ name_writer, value
308
+ else
309
+ instance_variable_set "@#{name}", value
310
+ end
141
311
  end
142
312
  end
143
313
 
@@ -146,7 +316,13 @@ module JSON
146
316
  # Converts this object to a string (calling #to_s), converts
147
317
  # it to a JSON string, and returns the result. This is a fallback, if no
148
318
  # special method #to_json was defined for some object.
149
- def to_json(*) to_s.to_json end
319
+ def to_json(generator_state)
320
+ if generator_state.strict?
321
+ raise GeneratorError, "#{self.class} not allowed in JSON"
322
+ else
323
+ to_s.to_json
324
+ end
325
+ end
150
326
  end
151
327
 
152
328
  module Hash
@@ -155,48 +331,43 @@ module JSON
155
331
  # _state_ is a JSON::State object, that can also be used to configure the
156
332
  # produced JSON string output further.
157
333
  # _depth_ is used to find out nesting depth, to indent accordingly.
158
- def to_json(state = nil, depth = 0, *)
159
- if state
160
- state = JSON.state.from_state(state)
161
- json_check_circular(state) { json_transform(state, depth) }
162
- else
163
- json_transform(state, depth)
164
- end
334
+ def to_json(state = nil, *)
335
+ state = State.from_state(state)
336
+ state.check_max_nesting
337
+ json_transform(state)
165
338
  end
166
339
 
167
340
  private
168
341
 
169
- def json_check_circular(state)
170
- if state
171
- state.seen?(self) and raise JSON::CircularDatastructure,
172
- "circular data structures not supported!"
173
- state.remember self
174
- end
175
- yield
176
- ensure
177
- state and state.forget self
342
+ def json_shift(state)
343
+ state.object_nl.empty? or return ''
344
+ state.indent * state.depth
178
345
  end
179
346
 
180
- def json_shift(state, depth)
181
- state and not state.object_nl.empty? or return ''
182
- state.indent * depth
183
- end
184
-
185
- def json_transform(state, depth)
186
- delim = ','
187
- delim << state.object_nl if state
188
- result = '{'
189
- result << state.object_nl if state
190
- result << map { |key,value|
191
- s = json_shift(state, depth + 1)
192
- s << key.to_s.to_json(state, depth + 1)
193
- s << state.space_before if state
194
- s << ':'
195
- s << state.space if state
196
- s << value.to_json(state, depth + 1)
197
- }.join(delim)
198
- result << state.object_nl if state
199
- result << json_shift(state, depth)
347
+ def json_transform(state)
348
+ delim = ",#{state.object_nl}"
349
+ result = "{#{state.object_nl}"
350
+ depth = state.depth += 1
351
+ first = true
352
+ indent = !state.object_nl.empty?
353
+ each { |key, value|
354
+ result << delim unless first
355
+ result << state.indent * depth if indent
356
+ result = "#{result}#{key.to_s.to_json(state)}#{state.space_before}:#{state.space}"
357
+ if state.strict?
358
+ raise GeneratorError, "#{value.class} not allowed in JSON"
359
+ elsif value.respond_to?(:to_json)
360
+ result << value.to_json(state)
361
+ else
362
+ result << %{"#{String(value)}"}
363
+ end
364
+ first = false
365
+ }
366
+ depth = state.depth -= 1
367
+ unless first
368
+ result << state.object_nl
369
+ result << state.indent * depth if indent
370
+ end
200
371
  result << '}'
201
372
  result
202
373
  end
@@ -207,46 +378,38 @@ module JSON
207
378
  # this Array instance.
208
379
  # _state_ is a JSON::State object, that can also be used to configure the
209
380
  # produced JSON string output further.
210
- # _depth_ is used to find out nesting depth, to indent accordingly.
211
- def to_json(state = nil, depth = 0, *)
212
- if state
213
- state = JSON.state.from_state(state)
214
- json_check_circular(state) { json_transform(state, depth) }
215
- else
216
- json_transform(state, depth)
217
- end
381
+ def to_json(state = nil, *)
382
+ state = State.from_state(state)
383
+ state.check_max_nesting
384
+ json_transform(state)
218
385
  end
219
386
 
220
387
  private
221
388
 
222
- def json_check_circular(state)
223
- if state
224
- state.seen?(self) and raise JSON::CircularDatastructure,
225
- "circular data structures not supported!"
226
- state.remember self
227
- end
228
- yield
229
- ensure
230
- state and state.forget self
231
- end
232
-
233
- def json_shift(state, depth)
234
- state and not state.array_nl.empty? or return ''
235
- state.indent * depth
236
- end
237
-
238
- def json_transform(state, depth)
389
+ def json_transform(state)
239
390
  delim = ','
240
- delim << state.array_nl if state
391
+ delim << state.array_nl
241
392
  result = '['
242
- result << state.array_nl if state
243
- result << map { |value|
244
- json_shift(state, depth + 1) << value.to_json(state, depth + 1)
245
- }.join(delim)
246
- result << state.array_nl if state
247
- result << json_shift(state, depth)
393
+ result << state.array_nl
394
+ depth = state.depth += 1
395
+ first = true
396
+ indent = !state.array_nl.empty?
397
+ each { |value|
398
+ result << delim unless first
399
+ result << state.indent * depth if indent
400
+ if state.strict?
401
+ raise GeneratorError, "#{value.class} not allowed in JSON"
402
+ elsif value.respond_to?(:to_json)
403
+ result << value.to_json(state)
404
+ else
405
+ result << %{"#{String(value)}"}
406
+ end
407
+ first = false
408
+ }
409
+ depth = state.depth -= 1
410
+ result << state.array_nl
411
+ result << state.indent * depth if indent
248
412
  result << ']'
249
- result
250
413
  end
251
414
  end
252
415
 
@@ -257,22 +420,51 @@ module JSON
257
420
 
258
421
  module Float
259
422
  # Returns a JSON string representation for this Float number.
260
- def to_json(*) to_s end
423
+ def to_json(state = nil, *)
424
+ state = State.from_state(state)
425
+ case
426
+ when infinite?
427
+ if state.allow_nan?
428
+ to_s
429
+ else
430
+ raise GeneratorError, "#{self} not allowed in JSON"
431
+ end
432
+ when nan?
433
+ if state.allow_nan?
434
+ to_s
435
+ else
436
+ raise GeneratorError, "#{self} not allowed in JSON"
437
+ end
438
+ else
439
+ to_s
440
+ end
441
+ end
261
442
  end
262
443
 
263
444
  module String
264
445
  # This string should be encoded with UTF-8 A call to this method
265
446
  # returns a JSON string encoded with UTF16 big endian characters as
266
447
  # \u????.
267
- def to_json(*)
268
- '"' << JSON.utf8_to_json(self) << '"'
448
+ def to_json(state = nil, *args)
449
+ state = State.from_state(state)
450
+ if encoding == ::Encoding::UTF_8
451
+ string = self
452
+ else
453
+ string = encode(::Encoding::UTF_8)
454
+ end
455
+ if state.ascii_only?
456
+ '"' << JSON.utf8_to_json_ascii(string, state.script_safe) << '"'
457
+ else
458
+ '"' << JSON.utf8_to_json(string, state.script_safe) << '"'
459
+ end
269
460
  end
270
461
 
271
- # Module that holds the extinding methods if, the String module is
462
+ # Module that holds the extending methods if, the String module is
272
463
  # included.
273
464
  module Extend
274
- # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
275
- # key "raw"). The Ruby String can be created by this module method.
465
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
466
+ # array for the key "raw"). The Ruby String can be created by this
467
+ # module method.
276
468
  def json_create(o)
277
469
  o['raw'].pack('C*')
278
470
  end