Unable to use dot syntax for ruby hash
Solution 1
Hash
does not have dot-syntax for it's keys. OpenStruct
does:
require 'ostruct'
hash = {:name => 'John'}
os = OpenStruct.new(hash)
p os.name #=> "John"
NOTE: Does not work with nested hashes.
Solution 2
OpenStruct will work well for a pure hash, but for hashes with embeded arrays or other hashes, the dot syntax will choke. I came across this solution, which works well without loading in another gem: https://coderwall.com/p/74rajw/convert-a-complex-nested-hash-to-an-object basic steps are:
data = YAML::load(File.open("your yaml file"))
json_data = data.to_json
mystr = JSON.parse(json_data,object_class: OpenStruct)
you can now access all objects in mystr using dot syntax.
Solution 3
Ruby hashes don't work like this natively, but the HashDot gem would work for this.
HashDot allows dot notation syntax use on hashes. It also works on json strings that have been re-parsed with JSON.parse
.
require 'hash_dot'
hash = {b: {c: {d: 1}}}.to_dot
hash.b.c.d => 1
json_hash = JSON.parse(hash.to_json)
json_hash.b.c.d => 1
Solution 4
Why not, you can do this via metaprogramming
module LookLikeJSON
def method_missing(meth, *args, &block)
if has_key?(meth.to_s)
self[meth.to_s]
else
raise NoMethodError, 'undefined method #{meth} for #{self}'
end
end
end
h = {"processingTime"=>"0.001493", "version"=>"1.4.0.526 build 111113", "documentLength"=>"25"}
h.extend(LookLikeJSON)
h.processingTime #=> "0.001493"
Solution 5
If you don't want to install any gems, you can try to use the Ruby's native Struct
class and some Ruby tricks, like the splat operator.
# regular hashes
customer = { name: "Maria", age: 21, country: "Brazil" }
customer.name
# => NoMethodError: undefined method `name' for {:name=>"Maria", :age=>21, :country=>"Brazil"}:Hash
# converting a hash to a struct
customer_on_steroids = Struct.new(*customer.keys).new(*customer.values)
customer_on_steroids.name
#=> "Maria"
Please note that this simple solution works only for single-level hashes. To make it dynamic and fully functional for any kind of Hash
, you'll have to make it recursive to create substructs inside your struct.
You can also store the Struct as if it was a class.
customer_1 = { name: "Maria", age: 21, country: "Brazil" }
customer_2 = { name: "João", age: 32, country: "Brazil" }
customer_3 = { name: "José", age: 43, country: "Brazil" }
Customer = Struct.new(*customer_1.keys)
customer_on_steroids_1 = Customer.new(*customer_1.values)
customer_on_steroids_2 = Customer.new(*customer_2.values)
customer_on_steroids_3 = Customer.new(*customer_3.values)
![Kyle Decot](https://i.stack.imgur.com/WvUmQ.jpg?s=256&g=1)
Kyle Decot
I'm a Senior Software Engineer at ROOT Insurance in Columbus Ohio. We use Ruby/Rails, React Native, PostgreSQL, Swift, Objective-C, and Java but I also enjoy exploring Erlang and Elixir.
Updated on July 24, 2022Comments
-
Kyle Decot almost 2 years
I'm using
net/http
to pull in some json data from the Yahoo Placemaker API. After receiving the response I am performingJSON.parse
on the response. This gives me a hash that looks like:{"processingTime"=>"0.001493", "version"=>"1.4.0.526 build 111113", "documentLength"=>"25", "document"=>{"administrativeScope"=>{"woeId"=>"2503863", "type"=>"Town", "name"=>"Tampa, FL, US", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}}, "geographicScope"=>{"woeId"=>"2503863", "type"=>"Town", "name"=>"Tampa, FL, US", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}}, "localScopes"=>{"localScope"=>{"woeId"=>"2503863", "type"=>"Town", "name"=>"Tampa, FL, US (Town)", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}, "southWest"=>{"latitude"=>"27.8132", "longitude"=>"-82.6489"}, "northEast"=>{"latitude"=>"28.1714", "longitude"=>"-82.2539"}, "ancestors"=>[{"ancestor"=>{"woeId"=>"12587831", "type"=>"County", "name"=>"Hillsborough"}}, {"ancestor"=>{"woeId"=>"2347568", "type"=>"State", "name"=>"Florida"}}, {"ancestor"=>{"woeId"=>"23424977", "type"=>"Country", "name"=>"United States"}}]}}, "extents"=>{"center"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}, "southWest"=>{"latitude"=>"27.8132", "longitude"=>"-82.6489"}, "northEast"=>{"latitude"=>"28.1714", "longitude"=>"-82.2539"}}, "placeDetails"=>{"placeId"=>"1", "place"=>{"woeId"=>"2503863", "type"=>"Town", "name"=>"Tampa, FL, US", "centroid"=>{"latitude"=>"27.9465", "longitude"=>"-82.4593"}}, "placeReferenceIds"=>"1", "matchType"=>"0", "weight"=>"1", "confidence"=>"8"}, "referenceList"=>{"reference"=>{"woeIds"=>"2503863", "placeReferenceId"=>"1", "placeIds"=>"1", "start"=>"15", "end"=>"20", "isPlaintextMarker"=>"1", "text"=>"Tampa", "type"=>"plaintext", "xpath"=>""}}}}
I am able to access elements by doing things like
jsonResponse['version']
but I am not able to dojsonResponse.version
. Why is this?