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/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