json_pure 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +18 -0
- data/Rakefile +5 -5
- data/VERSION +1 -1
- data/benchmarks/benchmark_generator.rb +5 -1
- data/benchmarks/benchmark_parser.rb +5 -1
- data/bin/edit_json.rb +11 -11
- data/ext/json/ext/generator/extconf.rb +2 -2
- data/ext/json/ext/generator/generator.c +185 -50
- data/ext/json/ext/parser/extconf.rb +2 -2
- data/ext/json/ext/parser/parser.c +246 -117
- data/ext/json/ext/parser/parser.rl +54 -6
- data/lib/json.rb +23 -0
- data/lib/json/add/core.rb +119 -0
- data/lib/json/add/rails.rb +52 -0
- data/lib/json/common.rb +174 -23
- data/lib/json/editor.rb +12 -14
- data/lib/json/pure/generator.rb +83 -10
- data/lib/json/pure/parser.rb +15 -1
- data/lib/json/version.rb +1 -1
- data/tests/fixtures/fail18.json +1 -1
- data/tests/test_json.rb +51 -12
- data/tests/test_json_addition.rb +34 -3
- data/tests/test_json_fixtures.rb +2 -2
- data/tests/test_json_generate.rb +21 -2
- data/tests/test_json_unicode.rb +4 -1
- data/tools/fuzz.rb +2 -1
- metadata +6 -3
data/lib/json/editor.rb
CHANGED
@@ -52,14 +52,12 @@ module JSON
|
|
52
52
|
MessageDialog::ERROR,
|
53
53
|
MessageDialog::BUTTONS_CLOSE, text)
|
54
54
|
dialog.show_all
|
55
|
-
window.focus = dialog
|
56
55
|
dialog.run
|
57
56
|
rescue TypeError
|
58
57
|
dialog = MessageDialog.new(Editor.window, Dialog::MODAL,
|
59
58
|
MessageDialog::ERROR,
|
60
59
|
MessageDialog::BUTTONS_CLOSE, text)
|
61
60
|
dialog.show_all
|
62
|
-
window.focus = dialog
|
63
61
|
dialog.run
|
64
62
|
ensure
|
65
63
|
dialog.destroy if dialog
|
@@ -73,7 +71,6 @@ module JSON
|
|
73
71
|
MessageDialog::QUESTION,
|
74
72
|
MessageDialog::BUTTONS_YES_NO, text)
|
75
73
|
dialog.show_all
|
76
|
-
window.focus = dialog
|
77
74
|
dialog.run do |response|
|
78
75
|
return Gtk::Dialog::RESPONSE_YES === response
|
79
76
|
end
|
@@ -1102,9 +1099,11 @@ module JSON
|
|
1102
1099
|
# Quit this editor, that is, leave this editor's main loop.
|
1103
1100
|
def quit
|
1104
1101
|
ask_save if @changed
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1102
|
+
if Gtk.main_level > 0
|
1103
|
+
destroy
|
1104
|
+
Gtk.main_quit
|
1105
|
+
end
|
1106
|
+
nil
|
1108
1107
|
end
|
1109
1108
|
|
1110
1109
|
# Display the new title according to the editor's current state.
|
@@ -1185,20 +1184,21 @@ module JSON
|
|
1185
1184
|
if filename
|
1186
1185
|
if File.directory?(filename)
|
1187
1186
|
Editor.error_dialog(self, "Try to select a JSON file!")
|
1188
|
-
|
1187
|
+
nil
|
1189
1188
|
else
|
1190
|
-
data = read_data(filename)
|
1191
1189
|
@filename = filename
|
1192
|
-
|
1190
|
+
if data = read_data(filename)
|
1191
|
+
toplevel.display_status("Loaded data from '#@filename'.")
|
1192
|
+
end
|
1193
1193
|
display_title
|
1194
|
-
|
1194
|
+
data
|
1195
1195
|
end
|
1196
1196
|
end
|
1197
1197
|
end
|
1198
1198
|
|
1199
1199
|
# Load the data at location _uri_ into the editor as a JSON document.
|
1200
1200
|
def load_location(uri)
|
1201
|
-
data = read_data(uri)
|
1201
|
+
data = read_data(uri) or return
|
1202
1202
|
@filename = nil
|
1203
1203
|
toplevel.display_status("Loaded data from '#{uri}'.")
|
1204
1204
|
display_title
|
@@ -1217,11 +1217,9 @@ module JSON
|
|
1217
1217
|
end
|
1218
1218
|
return JSON::parse(json, :max_nesting => false)
|
1219
1219
|
end
|
1220
|
-
rescue
|
1220
|
+
rescue => e
|
1221
1221
|
Editor.error_dialog(self, "Failed to parse JSON file: #{e}!")
|
1222
1222
|
return
|
1223
|
-
rescue SystemCallError => e
|
1224
|
-
quit
|
1225
1223
|
end
|
1226
1224
|
|
1227
1225
|
# Open a file selecton dialog, displaying _message_, and return the
|
data/lib/json/pure/generator.rb
CHANGED
@@ -89,15 +89,22 @@ module JSON
|
|
89
89
|
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
|
90
90
|
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
|
91
91
|
# * *check_circular*: true if checking for circular data structures
|
92
|
+
# should be done (the default), false otherwise.
|
93
|
+
# * *check_circular*: true if checking for circular data structures
|
92
94
|
# should be done, false (the default) otherwise.
|
95
|
+
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
96
|
+
# generated, otherwise an exception is thrown, if these values are
|
97
|
+
# encountered. This options defaults to false.
|
93
98
|
def initialize(opts = {})
|
94
|
-
@
|
95
|
-
@
|
96
|
-
@
|
97
|
-
@
|
98
|
-
@
|
99
|
-
@
|
100
|
-
@
|
99
|
+
@seen = {}
|
100
|
+
@indent = ''
|
101
|
+
@space = ''
|
102
|
+
@space_before = ''
|
103
|
+
@object_nl = ''
|
104
|
+
@array_nl = ''
|
105
|
+
@check_circular = true
|
106
|
+
@allow_nan = false
|
107
|
+
configure opts
|
101
108
|
end
|
102
109
|
|
103
110
|
# This string is used to indent levels in the JSON text.
|
@@ -117,12 +124,29 @@ module JSON
|
|
117
124
|
# This string is put at the end of a line that holds a JSON array.
|
118
125
|
attr_accessor :array_nl
|
119
126
|
|
127
|
+
# This integer returns the maximum level of data structure nesting in
|
128
|
+
# the generated JSON, max_nesting = 0 if no maximum is checked.
|
129
|
+
attr_accessor :max_nesting
|
130
|
+
|
131
|
+
def check_max_nesting(depth) # :nodoc:
|
132
|
+
return if @max_nesting.zero?
|
133
|
+
current_nesting = depth + 1
|
134
|
+
current_nesting > @max_nesting and
|
135
|
+
raise NestingError, "nesting of #{current_nesting} is too deep"
|
136
|
+
end
|
137
|
+
|
120
138
|
# Returns true, if circular data structures should be checked,
|
121
139
|
# otherwise returns false.
|
122
140
|
def check_circular?
|
123
141
|
@check_circular
|
124
142
|
end
|
125
143
|
|
144
|
+
# Returns true if NaN, Infinity, and -Infinity should be considered as
|
145
|
+
# valid JSON and output.
|
146
|
+
def allow_nan?
|
147
|
+
@allow_nan
|
148
|
+
end
|
149
|
+
|
126
150
|
# Returns _true_, if _object_ was already seen during this generating
|
127
151
|
# run.
|
128
152
|
def seen?(object)
|
@@ -139,6 +163,36 @@ module JSON
|
|
139
163
|
def forget(object)
|
140
164
|
@seen.delete object.__id__
|
141
165
|
end
|
166
|
+
|
167
|
+
# Configure this State instance with the Hash _opts_, and return
|
168
|
+
# itself.
|
169
|
+
def configure(opts)
|
170
|
+
@indent = opts[:indent] if opts.key?(:indent)
|
171
|
+
@space = opts[:space] if opts.key?(:space)
|
172
|
+
@space_before = opts[:space_before] if opts.key?(:space_before)
|
173
|
+
@object_nl = opts[:object_nl] if opts.key?(:object_nl)
|
174
|
+
@array_nl = opts[:array_nl] if opts.key?(:array_nl)
|
175
|
+
@check_circular = !!opts[:check_circular] if opts.key?(:check_circular)
|
176
|
+
@allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
|
177
|
+
if !opts.key?(:max_nesting) # defaults to 19
|
178
|
+
@max_nesting = 19
|
179
|
+
elsif opts[:max_nesting]
|
180
|
+
@max_nesting = opts[:max_nesting]
|
181
|
+
else
|
182
|
+
@max_nesting = 0
|
183
|
+
end
|
184
|
+
self
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns the configuration instance variables as a hash, that can be
|
188
|
+
# passed to the configure method.
|
189
|
+
def to_h
|
190
|
+
result = {}
|
191
|
+
for iv in %w[indent space space_before object_nl array_nl check_circular allow_nan max_nesting]
|
192
|
+
result[iv.intern] = instance_variable_get("@#{iv}")
|
193
|
+
end
|
194
|
+
result
|
195
|
+
end
|
142
196
|
end
|
143
197
|
|
144
198
|
module GeneratorMethods
|
@@ -158,6 +212,7 @@ module JSON
|
|
158
212
|
def to_json(state = nil, depth = 0, *)
|
159
213
|
if state
|
160
214
|
state = JSON.state.from_state(state)
|
215
|
+
state.check_max_nesting(depth)
|
161
216
|
json_check_circular(state) { json_transform(state, depth) }
|
162
217
|
else
|
163
218
|
json_transform(state, depth)
|
@@ -167,7 +222,7 @@ module JSON
|
|
167
222
|
private
|
168
223
|
|
169
224
|
def json_check_circular(state)
|
170
|
-
if state
|
225
|
+
if state and state.check_circular?
|
171
226
|
state.seen?(self) and raise JSON::CircularDatastructure,
|
172
227
|
"circular data structures not supported!"
|
173
228
|
state.remember self
|
@@ -211,6 +266,7 @@ module JSON
|
|
211
266
|
def to_json(state = nil, depth = 0, *)
|
212
267
|
if state
|
213
268
|
state = JSON.state.from_state(state)
|
269
|
+
state.check_max_nesting(depth)
|
214
270
|
json_check_circular(state) { json_transform(state, depth) }
|
215
271
|
else
|
216
272
|
json_transform(state, depth)
|
@@ -220,7 +276,7 @@ module JSON
|
|
220
276
|
private
|
221
277
|
|
222
278
|
def json_check_circular(state)
|
223
|
-
if state
|
279
|
+
if state and state.check_circular?
|
224
280
|
state.seen?(self) and raise JSON::CircularDatastructure,
|
225
281
|
"circular data structures not supported!"
|
226
282
|
state.remember self
|
@@ -257,7 +313,24 @@ module JSON
|
|
257
313
|
|
258
314
|
module Float
|
259
315
|
# Returns a JSON string representation for this Float number.
|
260
|
-
def to_json(*)
|
316
|
+
def to_json(state = nil, *)
|
317
|
+
case
|
318
|
+
when infinite?
|
319
|
+
if !state || state.allow_nan?
|
320
|
+
to_s
|
321
|
+
else
|
322
|
+
raise GeneratorError, "#{self} not allowed in JSON"
|
323
|
+
end
|
324
|
+
when nan?
|
325
|
+
if !state || state.allow_nan?
|
326
|
+
to_s
|
327
|
+
else
|
328
|
+
raise GeneratorError, "#{self} not allowed in JSON"
|
329
|
+
end
|
330
|
+
else
|
331
|
+
to_s
|
332
|
+
end
|
333
|
+
end
|
261
334
|
end
|
262
335
|
|
263
336
|
module String
|
data/lib/json/pure/parser.rb
CHANGED
@@ -19,6 +19,9 @@ module JSON
|
|
19
19
|
(?i:e[+-]?\d+)
|
20
20
|
)
|
21
21
|
)/x
|
22
|
+
NAN = /NaN/
|
23
|
+
INFINITY = /Infinity/
|
24
|
+
MINUS_INFINITY = /-Infinity/
|
22
25
|
OBJECT_OPEN = /\{/
|
23
26
|
OBJECT_CLOSE = /\}/
|
24
27
|
ARRAY_OPEN = /\[/
|
@@ -50,7 +53,11 @@ module JSON
|
|
50
53
|
# It will be configured by the _opts_ hash. _opts_ can have the following
|
51
54
|
# keys:
|
52
55
|
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
|
53
|
-
# structures. Disable depth checking with :max_nesting => false
|
56
|
+
# structures. Disable depth checking with :max_nesting => false|nil|0,
|
57
|
+
# it defaults to 19.
|
58
|
+
# * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
|
59
|
+
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
|
60
|
+
# to false.
|
54
61
|
def initialize(source, opts = {})
|
55
62
|
super
|
56
63
|
if !opts.key?(:max_nesting) # defaults to 19
|
@@ -60,6 +67,7 @@ module JSON
|
|
60
67
|
else
|
61
68
|
@max_nesting = 0
|
62
69
|
end
|
70
|
+
@allow_nan = !!opts[:allow_nan]
|
63
71
|
@create_id = JSON.create_id
|
64
72
|
end
|
65
73
|
|
@@ -153,6 +161,12 @@ module JSON
|
|
153
161
|
obj = parse_object
|
154
162
|
@current_nesting -= 1
|
155
163
|
obj
|
164
|
+
when @allow_nan && scan(NAN)
|
165
|
+
NaN
|
166
|
+
when @allow_nan && scan(INFINITY)
|
167
|
+
Infinity
|
168
|
+
when @allow_nan && scan(MINUS_INFINITY)
|
169
|
+
MinusInfinity
|
156
170
|
else
|
157
171
|
UNPARSED
|
158
172
|
end
|
data/lib/json/version.rb
CHANGED
data/tests/fixtures/fail18.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
|
1
|
+
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
|
data/tests/test_json.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'test/unit'
|
4
4
|
require 'json'
|
5
|
+
require 'stringio'
|
5
6
|
|
6
7
|
class TC_JSON < Test::Unit::TestCase
|
7
8
|
include JSON
|
@@ -61,6 +62,12 @@ class TC_JSON < Test::Unit::TestCase
|
|
61
62
|
assert_equal_float [3.141], parse('[3141.0E-3]')
|
62
63
|
assert_equal_float [-3.141], parse('[-3141.0e-3]')
|
63
64
|
assert_equal_float [-3.141], parse('[-3141e-3]')
|
65
|
+
assert_raises(ParserError) { parse('[NaN]') }
|
66
|
+
assert parse('[NaN]', :allow_nan => true).first.nan?
|
67
|
+
assert_raises(ParserError) { parse('[Infinity]') }
|
68
|
+
assert_equal [1.0/0], parse('[Infinity]', :allow_nan => true)
|
69
|
+
assert_raises(ParserError) { parse('[-Infinity]') }
|
70
|
+
assert_equal [-1.0/0], parse('[-Infinity]', :allow_nan => true)
|
64
71
|
assert_equal([""], parse('[""]'))
|
65
72
|
assert_equal(["foobar"], parse('["foobar"]'))
|
66
73
|
assert_equal([{}], parse('[{}]'))
|
@@ -238,18 +245,50 @@ EOT
|
|
238
245
|
end
|
239
246
|
|
240
247
|
def test_nesting
|
241
|
-
|
242
|
-
assert_raises(JSON::NestingError) { JSON.parse
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
ok = JSON.parse
|
250
|
-
|
251
|
-
ok = JSON.parse
|
252
|
-
|
248
|
+
assert_raises(JSON::NestingError) { JSON.parse '[[]]', :max_nesting => 1 }
|
249
|
+
assert_raises(JSON::NestingError) { JSON.parser.new('[[]]', :max_nesting => 1).parse }
|
250
|
+
assert_equal [[]], JSON.parse('[[]]', :max_nesting => 2)
|
251
|
+
too_deep = '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]'
|
252
|
+
too_deep_ary = eval too_deep
|
253
|
+
assert_raises(JSON::NestingError) { JSON.parse too_deep }
|
254
|
+
assert_raises(JSON::NestingError) { JSON.parser.new(too_deep).parse }
|
255
|
+
assert_raises(JSON::NestingError) { JSON.parse too_deep, :max_nesting => 19 }
|
256
|
+
ok = JSON.parse too_deep, :max_nesting => 20
|
257
|
+
assert_equal too_deep_ary, ok
|
258
|
+
ok = JSON.parse too_deep, :max_nesting => nil
|
259
|
+
assert_equal too_deep_ary, ok
|
260
|
+
ok = JSON.parse too_deep, :max_nesting => false
|
261
|
+
assert_equal too_deep_ary, ok
|
262
|
+
ok = JSON.parse too_deep, :max_nesting => 0
|
263
|
+
assert_equal too_deep_ary, ok
|
264
|
+
assert_raises(JSON::NestingError) { JSON.generate [[]], :max_nesting => 1 }
|
265
|
+
assert_equal '[[]]', JSON.generate([[]], :max_nesting => 2)
|
266
|
+
assert_raises(JSON::NestingError) { JSON.generate too_deep_ary }
|
267
|
+
assert_raises(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 19 }
|
268
|
+
ok = JSON.generate too_deep_ary, :max_nesting => 20
|
269
|
+
assert_equal too_deep, ok
|
270
|
+
ok = JSON.generate too_deep_ary, :max_nesting => nil
|
271
|
+
assert_equal too_deep, ok
|
272
|
+
ok = JSON.generate too_deep_ary, :max_nesting => false
|
273
|
+
assert_equal too_deep, ok
|
274
|
+
ok = JSON.generate too_deep_ary, :max_nesting => 0
|
275
|
+
assert_equal too_deep, ok
|
276
|
+
end
|
277
|
+
|
278
|
+
def test_load_dump
|
279
|
+
too_deep = '[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]'
|
280
|
+
assert_equal too_deep, JSON.dump(eval(too_deep))
|
281
|
+
assert_kind_of String, Marshal.dump(eval(too_deep))
|
282
|
+
assert_raises(ArgumentError) { JSON.dump(eval(too_deep), 19) }
|
283
|
+
assert_raises(ArgumentError) { Marshal.dump(eval(too_deep), 19) }
|
284
|
+
assert_equal too_deep, JSON.dump(eval(too_deep), 20)
|
285
|
+
assert_kind_of String, Marshal.dump(eval(too_deep), 20)
|
286
|
+
output = StringIO.new
|
287
|
+
JSON.dump(eval(too_deep), output)
|
288
|
+
assert_equal too_deep, output.string
|
289
|
+
output = StringIO.new
|
290
|
+
JSON.dump(eval(too_deep), output, 20)
|
291
|
+
assert_equal too_deep, output.string
|
253
292
|
end
|
254
293
|
end
|
255
294
|
# vim: set et sw=2 ts=2:
|
data/tests/test_json_addition.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'test/unit'
|
4
|
-
require 'json'
|
4
|
+
require 'json/add/core'
|
5
|
+
require 'date'
|
5
6
|
|
6
7
|
class TC_JSONAddition < Test::Unit::TestCase
|
7
8
|
include JSON
|
@@ -23,7 +24,7 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|
23
24
|
|
24
25
|
def to_json(*args)
|
25
26
|
{
|
26
|
-
'json_class' => self.class,
|
27
|
+
'json_class' => self.class.name,
|
27
28
|
'args' => [ @a ],
|
28
29
|
}.to_json(*args)
|
29
30
|
end
|
@@ -32,7 +33,7 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|
32
33
|
class B
|
33
34
|
def to_json(*args)
|
34
35
|
{
|
35
|
-
'json_class' => self.class,
|
36
|
+
'json_class' => self.class.name,
|
36
37
|
}.to_json(*args)
|
37
38
|
end
|
38
39
|
end
|
@@ -91,4 +92,34 @@ EOT
|
|
91
92
|
raw_again = JSON.parse(json)
|
92
93
|
assert_equal raw, raw_again
|
93
94
|
end
|
95
|
+
|
96
|
+
def test_core
|
97
|
+
t = Time.now
|
98
|
+
assert_equal t, JSON(JSON(t))
|
99
|
+
d = Date.today
|
100
|
+
assert_equal d, JSON(JSON(d))
|
101
|
+
d = DateTime.civil(2007, 6, 14, 14, 57, 10, Rational(1, 12), 2299161)
|
102
|
+
assert_equal d, JSON(JSON(d))
|
103
|
+
assert_equal 1..10, JSON(JSON(1..10))
|
104
|
+
assert_equal 1...10, JSON(JSON(1...10))
|
105
|
+
assert_equal "a".."c", JSON(JSON("a".."c"))
|
106
|
+
assert_equal "a"..."c", JSON(JSON("a"..."c"))
|
107
|
+
struct = Struct.new 'MyStruct', :foo, :bar
|
108
|
+
s = struct.new 4711, 'foot'
|
109
|
+
assert_equal s, JSON(JSON(s))
|
110
|
+
struct = Struct.new :foo, :bar
|
111
|
+
s = struct.new 4711, 'foot'
|
112
|
+
assert_raises(JSONError) { JSON(s) }
|
113
|
+
begin
|
114
|
+
raise TypeError, "test me"
|
115
|
+
rescue TypeError => e
|
116
|
+
e_json = JSON.generate e
|
117
|
+
e_again = JSON e_json
|
118
|
+
assert_kind_of TypeError, e_again
|
119
|
+
assert_equal e.message, e_again.message
|
120
|
+
assert_equal e.backtrace, e_again.backtrace
|
121
|
+
end
|
122
|
+
assert_equal /foo/, JSON(JSON(/foo/))
|
123
|
+
assert_equal /foo/i, JSON(JSON(/foo/i))
|
124
|
+
end
|
94
125
|
end
|
data/tests/test_json_fixtures.rb
CHANGED
@@ -13,14 +13,14 @@ class TC_JSONFixtures < Test::Unit::TestCase
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_passing
|
16
|
-
for
|
16
|
+
for name, source in @passed
|
17
17
|
assert JSON.parse(source),
|
18
18
|
"Did not pass for fixture '#{name}'"
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_failing
|
23
|
-
for
|
23
|
+
for name, source in @failed
|
24
24
|
assert_raises(JSON::ParserError, JSON::NestingError,
|
25
25
|
"Did not fail for fixture '#{name}'") do
|
26
26
|
JSON.parse(source)
|