Issue #11762 has been updated by Colin Kelley.


> If #dig returns nil instead of exception, as you want, we cannot distinguish case 2 and case 3.

I've looked at a lot of JSON parsing code in Ruby and haven't found examples that were looking to draw that distinction.  (Those that do would probably use a gem like [ruby-json-schema](:https://github.com/ruby-json-schema/json-schema) to validate the precise format and get a precise exception.)

What I have found is code that navigates the nested JSON response and ignores the possibility of the exception `NoMethodError: undefined method '[]' for nil:NilClass` or `TypeError: no implicit conversion of Symbol into Integer` being raised during the navigation.  I looked at several examples in my company's codebase and the first two pages of results from the Google search "ruby json parse example" and all of them suffered from the problem.

Here is the first example from [Stackoverflow](http://stackoverflow.com/questions/5410682/parsing-a-json-string-in-ruby):

~~~
parsed = JSON.parse(string) # returns a hash

p parsed["desc"]["someKey"]
p parsed["main_item"]["stats"]["a"]
~~~

and one from [Temboo](https://temboo.com/ruby/parsing-json):

~~~
title = data["items"][0]["snippet"]["title"]
description = data["items"][0]["snippet"]["description"]
~~~

and one from [richonrails](https://richonrails.com/articles/parsing-json-in-ruby-on-rails):

~~~
h["results"][0]["name"] # => "ABC Widget 2.0"
~~~

and one from [developer.yahoo](https://developer.yahoo.com/ruby/ruby-json.html):

~~~
irb(main):003:0> news['ResultSet']['Result'].each { |result|
irb(main):004:1* print "#{result['Title']} => #{result['Url']}\n"
irb(main):005:1> }
~~~

and one from [ruby-forum](https://www.ruby-forum.com/topic/4411887):

~~~
  adbreaks = parsed['TOD']['AdBreaks']['AdBreak']
  lengths_of_breaks = adbreaks.map {|ad| ad['duration'] }
  list_of_30s_breaks = adbreaks.select {|ad| ad['duration'] == 30 }

... [and later from the original poster]

I am attempting to do the following:
uri = parsed['TOD']['AdBreaks']['AdBreak'][0]['Ad'][0]['MediaFile']['Impression']['Log'][0]['uri']

but it does not work. I get the following:  NoMethodError: undefined method `[]' for nil:NilClass
~~~

I couldn't find a single example in my sample that navigates nested JSON without stepping into the `undefined method '[]' for nil:NilClass` trap.  We dread that exception because it never has enough detail to debug the problem.

That's why I'm so eager to see `dig` unify the navigation to a single contract.  It could raise an explicit exception.  But I think it would be more idiomatic Ruby to return  `nil` if the value is not found.  This makes it natural for the typical calling case where the data is considered optional:

~~~
if a = parsed["main_item"]["stats"]["a"]
  ... # use a
end
~~~

or to raise an exception itself:

~~~
a = parsed["main_item"]["stats"]["a"] or raise "unexpected format #{parsed.inspect}"
~~~


----------------------------------------
Bug #11762: Array#dig can raise TypeError: no implicit conversion of Symbol/String into Integer
https://bugs.ruby-lang.org/issues/11762#change-55517

* Author: Colin Kelley
* Status: Closed
* Priority: Normal
* Assignee: Yukihiro Matsumoto
* ruby -v: 2.3.0-preview2
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
If you try to `dig` in an Array using a symbol or string, a `TypeError` exception will be raised:

irb> ['zero', 'one', 'two'].dig(:first)
TypeError: no implicit conversion of Symbol into Integer
    from (irb):1:in `dig'
    from (irb):1

I think it should return `nil` in this case.  The most typical use case for `dig` is to dig through parsed JSON and either find the result we expected or else `nil`.  Wouldn't it defeat the purpose of `dig` if we had to wrap calls to it in a `rescue` to handle the case that an Array was present where we expected a Hash?

Can we clarify the desired behavior for this case, then update the documentation and tests to reflect that?

---Files--------------------------------
11762.patch (3.19 KB)


-- 
https://bugs.ruby-lang.org/