json 1.2.4 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of json might be problematic. Click here for more details.

@@ -40,6 +40,15 @@ module JSON
40
40
  # UTF16 big endian characters as \u????, and return it.
41
41
  if defined?(::Encoding)
42
42
  def utf8_to_json(string) # :nodoc:
43
+ string = string.dup
44
+ string << '' # XXX workaround: avoid buffer sharing
45
+ string.force_encoding(::Encoding::ASCII_8BIT)
46
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
47
+ string.force_encoding(::Encoding::UTF_8)
48
+ string
49
+ end
50
+
51
+ def utf8_to_json_ascii(string) # :nodoc:
43
52
  string = string.dup
44
53
  string << '' # XXX workaround: avoid buffer sharing
45
54
  string.force_encoding(::Encoding::ASCII_8BIT)
@@ -63,6 +72,10 @@ module JSON
63
72
  end
64
73
  else
65
74
  def utf8_to_json(string) # :nodoc:
75
+ string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
76
+ end
77
+
78
+ def utf8_to_json_ascii(string) # :nodoc:
66
79
  string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
67
80
  string.gsub!(/(
68
81
  (?:
@@ -81,7 +94,7 @@ module JSON
81
94
  raise GeneratorError, "Caught #{e.class}: #{e}"
82
95
  end
83
96
  end
84
- module_function :utf8_to_json
97
+ module_function :utf8_to_json, :utf8_to_json_ascii
85
98
 
86
99
  module Pure
87
100
  module Generator
@@ -99,7 +112,7 @@ module JSON
99
112
  when Hash
100
113
  new(opts)
101
114
  else
102
- new
115
+ SAFE_STATE_PROTOTYPE
103
116
  end
104
117
  end
105
118
 
@@ -112,22 +125,20 @@ module JSON
112
125
  # * *space_before*: a string that is put before a : pair delimiter (default: ''),
113
126
  # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
114
127
  # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
115
- # * *check_circular*: true if checking for circular data structures
116
- # should be done (the default), false otherwise.
117
- # * *check_circular*: true if checking for circular data structures
118
- # should be done, false (the default) otherwise.
128
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
129
+ # * *max_nesting*: sets the maximum level of data structure nesting in
130
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
119
131
  # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
120
132
  # generated, otherwise an exception is thrown, if these values are
121
133
  # encountered. This options defaults to false.
122
134
  def initialize(opts = {})
123
- @seen = {}
124
135
  @indent = ''
125
136
  @space = ''
126
137
  @space_before = ''
127
138
  @object_nl = ''
128
139
  @array_nl = ''
129
- @check_circular = true
130
140
  @allow_nan = false
141
+ @ascii_only = false
131
142
  configure opts
132
143
  end
133
144
 
@@ -159,10 +170,10 @@ module JSON
159
170
  raise NestingError, "nesting of #{current_nesting} is too deep"
160
171
  end
161
172
 
162
- # Returns true, if circular data structures should be checked,
173
+ # Returns true, if circular data structures are checked,
163
174
  # otherwise returns false.
164
175
  def check_circular?
165
- @check_circular
176
+ !@max_nesting.zero?
166
177
  end
167
178
 
168
179
  # Returns true if NaN, Infinity, and -Infinity should be considered as
@@ -171,21 +182,8 @@ module JSON
171
182
  @allow_nan
172
183
  end
173
184
 
174
- # Returns _true_, if _object_ was already seen during this generating
175
- # run.
176
- def seen?(object)
177
- @seen.key?(object.__id__)
178
- end
179
-
180
- # Remember _object_, to find out if it was already encountered (if a
181
- # cyclic data structure is if a cyclic data structure is rendered).
182
- def remember(object)
183
- @seen[object.__id__] = true
184
- end
185
-
186
- # Forget _object_ for this generating run.
187
- def forget(object)
188
- @seen.delete object.__id__
185
+ def ascii_only?
186
+ @ascii_only
189
187
  end
190
188
 
191
189
  # Configure this State instance with the Hash _opts_, and return
@@ -196,8 +194,8 @@ module JSON
196
194
  @space_before = opts[:space_before] if opts.key?(:space_before)
197
195
  @object_nl = opts[:object_nl] if opts.key?(:object_nl)
198
196
  @array_nl = opts[:array_nl] if opts.key?(:array_nl)
199
- @check_circular = !!opts[:check_circular] if opts.key?(:check_circular)
200
197
  @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
198
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
201
199
  if !opts.key?(:max_nesting) # defaults to 19
202
200
  @max_nesting = 19
203
201
  elsif opts[:max_nesting]
@@ -212,12 +210,23 @@ module JSON
212
210
  # passed to the configure method.
213
211
  def to_h
214
212
  result = {}
215
- for iv in %w[indent space space_before object_nl array_nl check_circular allow_nan max_nesting]
213
+ for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting]
216
214
  result[iv.intern] = instance_variable_get("@#{iv}")
217
215
  end
218
216
  result
219
217
  end
220
218
 
219
+ # Generates a valid JSON document from object +obj+ and returns the
220
+ # result. If no valid JSON document can be created this method raises a
221
+ # GeneratorError exception.
222
+ def generate(obj)
223
+ result = obj.to_json(self)
224
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
225
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
226
+ end
227
+ result
228
+ end
229
+
221
230
  # Return the value returned by method +name+.
222
231
  def [](name)
223
232
  __send__ name
@@ -240,27 +249,14 @@ module JSON
240
249
  # _depth_ is used to find out nesting depth, to indent accordingly.
241
250
  def to_json(state = nil, depth = 0, *)
242
251
  if state
243
- state = JSON.state.from_state(state)
252
+ state = State.from_state(state)
244
253
  state.check_max_nesting(depth)
245
- json_check_circular(state) { json_transform(state, depth) }
246
- else
247
- json_transform(state, depth)
248
254
  end
255
+ json_transform(state, depth)
249
256
  end
250
257
 
251
258
  private
252
259
 
253
- def json_check_circular(state)
254
- if state and state.check_circular?
255
- state.seen?(self) and raise JSON::CircularDatastructure,
256
- "circular data structures not supported!"
257
- state.remember self
258
- end
259
- yield
260
- ensure
261
- state and state.forget self
262
- end
263
-
264
260
  def json_shift(state, depth)
265
261
  state and not state.object_nl.empty? or return ''
266
262
  state.indent * depth
@@ -272,16 +268,22 @@ module JSON
272
268
  delim << state.object_nl
273
269
  result = '{'
274
270
  result << state.object_nl
275
- result << map { |key,value|
276
- s = json_shift(state, depth + 1)
277
- s << key.to_s.to_json(state, depth + 1)
278
- s << state.space_before
279
- s << ':'
280
- s << state.space
281
- s << value.to_json(state, depth + 1)
282
- }.join(delim)
271
+ depth += 1
272
+ first = true
273
+ indent = state && !state.object_nl.empty?
274
+ each { |key,value|
275
+ result << delim unless first
276
+ result << state.indent * depth if indent
277
+ result << key.to_s.to_json(state, depth)
278
+ result << state.space_before
279
+ result << ':'
280
+ result << state.space
281
+ result << value.to_json(state, depth)
282
+ first = false
283
+ }
284
+ depth -= 1
283
285
  result << state.object_nl
284
- result << json_shift(state, depth)
286
+ result << state.indent * depth if indent if indent
285
287
  result << '}'
286
288
  else
287
289
  result = '{'
@@ -302,43 +304,32 @@ module JSON
302
304
  # _depth_ is used to find out nesting depth, to indent accordingly.
303
305
  def to_json(state = nil, depth = 0, *)
304
306
  if state
305
- state = JSON.state.from_state(state)
307
+ state = State.from_state(state)
306
308
  state.check_max_nesting(depth)
307
- json_check_circular(state) { json_transform(state, depth) }
308
- else
309
- json_transform(state, depth)
310
309
  end
310
+ json_transform(state, depth)
311
311
  end
312
312
 
313
313
  private
314
314
 
315
- def json_check_circular(state)
316
- if state and state.check_circular?
317
- state.seen?(self) and raise JSON::CircularDatastructure,
318
- "circular data structures not supported!"
319
- state.remember self
320
- end
321
- yield
322
- ensure
323
- state and state.forget self
324
- end
325
-
326
- def json_shift(state, depth)
327
- state and not state.array_nl.empty? or return ''
328
- state.indent * depth
329
- end
330
-
331
315
  def json_transform(state, depth)
332
316
  delim = ','
333
317
  if state
334
318
  delim << state.array_nl
335
319
  result = '['
336
320
  result << state.array_nl
337
- result << map { |value|
338
- json_shift(state, depth + 1) << value.to_json(state, depth + 1)
339
- }.join(delim)
321
+ depth += 1
322
+ first = true
323
+ indent = state && !state.array_nl.empty?
324
+ each { |value|
325
+ result << delim unless first
326
+ result << state.indent * depth if indent
327
+ result << value.to_json(state, depth)
328
+ first = false
329
+ }
330
+ depth -= 1
340
331
  result << state.array_nl
341
- result << json_shift(state, depth)
332
+ result << state.indent * depth if indent
342
333
  result << ']'
343
334
  else
344
335
  '[' << map { |value| value.to_json }.join(delim) << ']'
@@ -378,11 +369,17 @@ module JSON
378
369
  # This string should be encoded with UTF-8 A call to this method
379
370
  # returns a JSON string encoded with UTF16 big endian characters as
380
371
  # \u????.
381
- def to_json(*)
372
+ def to_json(*args)
373
+ state, = *args
374
+ state ||= State.from_state(state)
382
375
  if encoding == ::Encoding::UTF_8
383
- '"' << JSON.utf8_to_json(self) << '"'
376
+ string = self
384
377
  else
385
378
  string = encode(::Encoding::UTF_8)
379
+ end
380
+ if state.ascii_only?
381
+ '"' << JSON.utf8_to_json_ascii(string) << '"'
382
+ else
386
383
  '"' << JSON.utf8_to_json(string) << '"'
387
384
  end
388
385
  end
@@ -390,16 +387,23 @@ module JSON
390
387
  # This string should be encoded with UTF-8 A call to this method
391
388
  # returns a JSON string encoded with UTF16 big endian characters as
392
389
  # \u????.
393
- def to_json(*)
394
- '"' << JSON.utf8_to_json(self) << '"'
390
+ def to_json(*args)
391
+ state, = *args
392
+ state ||= State.from_state(state)
393
+ if state.ascii_only?
394
+ '"' << JSON.utf8_to_json_ascii(self) << '"'
395
+ else
396
+ '"' << JSON.utf8_to_json(self) << '"'
397
+ end
395
398
  end
396
399
  end
397
400
 
398
401
  # Module that holds the extinding methods if, the String module is
399
402
  # included.
400
403
  module Extend
401
- # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
402
- # key "raw"). The Ruby String can be created by this module method.
404
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
405
+ # array for the key "raw"). The Ruby String can be created by this
406
+ # module method.
403
407
  def json_create(o)
404
408
  o['raw'].pack('C*')
405
409
  end
@@ -1,6 +1,6 @@
1
1
  module JSON
2
2
  # JSON version
3
- VERSION = '1.2.4'
3
+ VERSION = '1.4.0'
4
4
  VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -57,11 +57,12 @@ class TC_JSONEncoding < Test::Unit::TestCase
57
57
  end
58
58
 
59
59
  def test_generate
60
- assert_equal @generated, JSON.generate(@parsed)
60
+ assert_equal @generated, JSON.generate(@parsed, :ascii_only => true)
61
61
  if defined?(::Encoding)
62
- assert_equal @generated, JSON.generate(@utf_16_data)
62
+ assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true)
63
63
  else
64
- assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data) }
64
+ # XXX checking of correct utf8 data is not as strict (yet?) without :ascii_only
65
+ assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data, :ascii_only => true) }
65
66
  end
66
67
  end
67
68
  end
@@ -87,19 +87,19 @@ EOT
87
87
  def test_states
88
88
  json = generate({1=>2}, nil)
89
89
  assert_equal('{"1":2}', json)
90
- s = JSON.state.new(:check_circular => true)
90
+ s = JSON.state.new
91
91
  assert s.check_circular?
92
92
  assert s[:check_circular?]
93
93
  h = { 1=>2 }
94
94
  h[3] = h
95
- assert_raises(JSON::CircularDatastructure) { generate(h) }
96
- assert_raises(JSON::CircularDatastructure) { generate(h, s) }
97
- s = JSON.state.new(:check_circular => true)
98
- assert s.check_circular?
99
- assert s[:check_circular?]
95
+ assert_raises(JSON::NestingError) { generate(h) }
96
+ assert_raises(JSON::NestingError) { generate(h, s) }
97
+ s = JSON.state.new
100
98
  a = [ 1, 2 ]
101
99
  a << a
102
- assert_raises(JSON::CircularDatastructure) { generate(a, s) }
100
+ assert_raises(JSON::NestingError) { generate(a, s) }
101
+ assert s.check_circular?
102
+ assert s[:check_circular?]
103
103
  end
104
104
 
105
105
  def test_allow_nan
@@ -19,22 +19,36 @@ class TC_JSONUnicode < Test::Unit::TestCase
19
19
  assert_equal '" "', ' '.to_json
20
20
  assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
21
21
  utf8 = [ "© ≠ €! \01" ]
22
+ json = '["© ≠ €! \u0001"]'
23
+ assert_equal json, utf8.to_json(:ascii_only => false)
24
+ assert_equal utf8, parse(json)
22
25
  json = '["\u00a9 \u2260 \u20ac! \u0001"]'
23
- assert_equal json, utf8.to_json
26
+ assert_equal json, utf8.to_json(:ascii_only => true)
27
+ assert_equal utf8, parse(json)
28
+ utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
29
+ json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]"
24
30
  assert_equal utf8, parse(json)
31
+ assert_equal json, utf8.to_json(:ascii_only => false)
25
32
  utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
33
+ assert_equal utf8, parse(json)
26
34
  json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
27
- assert_equal json, utf8.to_json
35
+ assert_equal json, utf8.to_json(:ascii_only => true)
28
36
  assert_equal utf8, parse(json)
29
37
  utf8 = ['საქართველო']
38
+ json = '["საქართველო"]'
39
+ assert_equal json, utf8.to_json(:ascii_only => false)
30
40
  json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
31
- assert_equal json, utf8.to_json
41
+ assert_equal json, utf8.to_json(:ascii_only => true)
32
42
  assert_equal utf8, parse(json)
33
- assert_equal '["\\u00c3"]', JSON.generate(["Ã"])
43
+ assert_equal '["Ã"]', JSON.generate(["Ã"], :ascii_only => false)
44
+ assert_equal '["\\u00c3"]', JSON.generate(["Ã"], :ascii_only => true)
34
45
  assert_equal ["€"], JSON.parse('["\u20ac"]')
35
46
  utf8 = ["\xf0\xa0\x80\x81"]
47
+ json = "[\"\xf0\xa0\x80\x81\"]"
48
+ assert_equal json, JSON.generate(utf8, :ascii_only => false)
49
+ assert_equal utf8, JSON.parse(json)
36
50
  json = '["\ud840\udc01"]'
37
- assert_equal json, JSON.generate(utf8)
51
+ assert_equal json, JSON.generate(utf8, :ascii_only => true)
38
52
  assert_equal utf8, JSON.parse(json)
39
53
  end
40
54
 
@@ -55,7 +69,7 @@ class TC_JSONUnicode < Test::Unit::TestCase
55
69
  end
56
70
  end
57
71
  assert_raise(JSON::GeneratorError) do
58
- JSON.generate(["\x80"])
72
+ JSON.generate(["\x80"], :ascii_only => true)
59
73
  end
60
74
  assert_equal "\302\200", JSON.parse('["\u0080"]').first
61
75
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-04-08 00:00:00 +02:00
12
+ date: 2010-04-23 00:00:00 +02:00
13
13
  default_executable: edit_json.rb
14
14
  dependencies: []
15
15
 
@@ -19,8 +19,8 @@ executables:
19
19
  - edit_json.rb
20
20
  - prettify_json.rb
21
21
  extensions:
22
- - ext/json/ext/generator/extconf.rb
23
- - ext/json/ext/parser/extconf.rb
22
+ - ext/json/ext/extconf_generator.rb
23
+ - ext/json/ext/extconf_parser.rb
24
24
  extra_rdoc_files:
25
25
  - README
26
26
  files:
@@ -31,6 +31,7 @@ files:
31
31
  - GPL
32
32
  - TODO
33
33
  - README
34
+ - benchmarks/ohai.json
34
35
  - benchmarks/parser_benchmark.rb
35
36
  - benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log
36
37
  - benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log
@@ -63,16 +64,17 @@ files:
63
64
  - benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat
64
65
  - benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat
65
66
  - benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat
67
+ - benchmarks/generator2_benchmark.rb
66
68
  - benchmarks/generator_benchmark.rb
67
- - ext/json/ext/generator/extconf.rb
68
- - ext/json/ext/generator/unicode.c
69
- - ext/json/ext/generator/generator.c
70
- - ext/json/ext/generator/unicode.h
71
- - ext/json/ext/parser/extconf.rb
72
- - ext/json/ext/parser/parser.rl
73
- - ext/json/ext/parser/unicode.c
74
- - ext/json/ext/parser/parser.c
75
- - ext/json/ext/parser/unicode.h
69
+ - benchmarks/parser2_benchmark.rb
70
+ - benchmarks/ohai.ruby
71
+ - ext/json/ext/extconf_generator.rb
72
+ - ext/json/ext/parser.rl
73
+ - ext/json/ext/extconf_parser.rb
74
+ - ext/json/ext/parser.h
75
+ - ext/json/ext/parser.c
76
+ - ext/json/ext/generator.c
77
+ - ext/json/ext/generator.h
76
78
  - Rakefile
77
79
  - tools/fuzz.rb
78
80
  - tools/server.rb
@@ -145,7 +147,7 @@ licenses: []
145
147
  post_install_message:
146
148
  rdoc_options:
147
149
  - --title
148
- - JSON -- A JSON implemention
150
+ - JSON implemention for Ruby
149
151
  - --main
150
152
  - README
151
153
  require_paths:
@@ -170,7 +172,7 @@ rubyforge_project: json
170
172
  rubygems_version: 1.3.5
171
173
  signing_key:
172
174
  specification_version: 3
173
- summary: A JSON implementation as a Ruby extension
175
+ summary: JSON Implementation for Ruby
174
176
  test_files:
175
177
  - tests/test_json_encoding.rb
176
178
  - tests/test_json_addition.rb