Skip to content

Commit 77292cb

Browse files
committed
Added testing for JSON.unsafe_load. Fixes NoMethodError when passing proc to JSON.unsafe_load, matching the changes made in 73d2137.
1 parent 036e30f commit 77292cb

File tree

3 files changed

+89
-3
lines changed

3 files changed

+89
-3
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
```
1616
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
1717
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
18+
* Fix `JSON.unsafe_load` usage with proc
1819

1920
### 2025-07-28 (2.13.2)
2021

lib/json/common.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,9 +703,13 @@ def unsafe_load(source, proc = nil, options = nil)
703703
if opts[:allow_blank] && (source.nil? || source.empty?)
704704
source = 'null'
705705
end
706-
result = parse(source, opts)
707-
recurse_proc(result, &proc) if proc
708-
result
706+
707+
if proc
708+
opts = opts.dup
709+
opts[:on_load] = proc.to_proc
710+
end
711+
712+
parse(source, opts)
709713
end
710714

711715
# :call-seq:

test/json/json_common_interface_test.rb

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,87 @@ def test_load_null
162162
assert_raise(JSON::ParserError) { JSON.load('', nil, :allow_blank => false) }
163163
end
164164

165+
def test_unsafe_load
166+
string_able_klass = Class.new do
167+
def initialize(str)
168+
@str = str
169+
end
170+
171+
def to_str
172+
@str
173+
end
174+
end
175+
176+
io_able_klass = Class.new do
177+
def initialize(str)
178+
@str = str
179+
end
180+
181+
def to_io
182+
StringIO.new(@str)
183+
end
184+
end
185+
186+
assert_equal @hash, JSON.unsafe_load(@json)
187+
tempfile = Tempfile.open('@json')
188+
tempfile.write @json
189+
tempfile.rewind
190+
assert_equal @hash, JSON.unsafe_load(tempfile)
191+
stringio = StringIO.new(@json)
192+
stringio.rewind
193+
assert_equal @hash, JSON.unsafe_load(stringio)
194+
string_able = string_able_klass.new(@json)
195+
assert_equal @hash, JSON.unsafe_load(string_able)
196+
io_able = io_able_klass.new(@json)
197+
assert_equal @hash, JSON.unsafe_load(io_able)
198+
assert_equal nil, JSON.unsafe_load(nil)
199+
assert_equal nil, JSON.unsafe_load('')
200+
ensure
201+
tempfile.close!
202+
end
203+
204+
def test_unsafe_load_with_proc
205+
visited = []
206+
JSON.unsafe_load('{"foo": [1, 2, 3], "bar": {"baz": "plop"}}', proc { |o| visited << JSON.dump(o); o })
207+
208+
expected = [
209+
'"foo"',
210+
'1',
211+
'2',
212+
'3',
213+
'[1,2,3]',
214+
'"bar"',
215+
'"baz"',
216+
'"plop"',
217+
'{"baz":"plop"}',
218+
'{"foo":[1,2,3],"bar":{"baz":"plop"}}',
219+
]
220+
assert_equal expected, visited
221+
end
222+
223+
def test_unsafe_load_default_options
224+
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
225+
assert JSON.unsafe_load(too_deep, nil).is_a?(Array)
226+
nan_json = '{ "foo": NaN }'
227+
assert JSON.unsafe_load(nan_json, nil)['foo'].nan?
228+
assert_equal nil, JSON.unsafe_load(nil, nil)
229+
t = Time.now
230+
assert_equal t, JSON.unsafe_load(JSON(t))
231+
end
232+
233+
def test_unsafe_load_with_options
234+
nan_json = '{ "foo": NaN }'
235+
assert_raise(JSON::ParserError) { JSON.unsafe_load(nan_json, nil, :allow_nan => false)['foo'].nan? }
236+
# make sure it still uses the defaults when something is provided
237+
assert JSON.unsafe_load(nan_json, nil, :allow_blank => true)['foo'].nan?
238+
end
239+
240+
def test_unsafe_load_null
241+
assert_equal nil, JSON.unsafe_load(nil, nil, :allow_blank => true)
242+
assert_raise(TypeError) { JSON.unsafe_load(nil, nil, :allow_blank => false) }
243+
assert_raise(JSON::ParserError) { JSON.unsafe_load('', nil, :allow_blank => false) }
244+
end
245+
165246
def test_dump
166247
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
167248
obj = eval(too_deep)

0 commit comments

Comments
 (0)