无法使用ruby哈希的点语法

我正在使用net/http从Yahoo Placemaker API中提取一些json数据。 收到响应后,我在响应上执行JSON.parse 。 这给了我一个看起来像这样的哈希:

 {"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"=>""}}}} 

我可以通过像jsonResponse['version']这样的东西来访问元素但是我无法做jsonResponse.version 。 为什么是这样?

Hash没有针对它的密钥的点语法。 OpenStruct做:

 require 'ostruct' hash = {:name => 'John'} os = OpenStruct.new(hash) p os.name #=> "John" 

OpenStruct适用于纯哈希,但对于嵌入数组或其他哈希的哈希,点语法会窒息。 我遇到了这个解决方案,它运行良好,无需加载另一个gem: https : //coderwall.com/p/74rajw/convert-a-complex-nested-hash-to-an-object基本步骤是:

 data = YAML::load(File.open("your yaml file")) json_data = data.to_json mystr = JSON.parse(json_data,object_class: OpenStruct) 

您现在可以使用点语法访问mystr中的所有对象。

HashDot gem可以为此工作。

HashDot允许在哈希上使用点表示法语法。 它也适用于已使用JSON.parse重新解析的json字符串。

 require 'hash_dot' hash = {b: {c: {d: 1}}}.to_dot hash.bcd => 1 json_hash = JSON.parse(hash.to_json) json_hash.bcd => 1 

这是一个JavaScriptfunction,而不是Rubyfunction。 在Ruby中,要使用“点语法”,对象需要响应这些方法。 Ruby哈希使用#[](key)方法来访问元素。

为什么不,你可以通过元编程来做到这一点

 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" 

因为Hash没有version方法。

如果你不想安装任何gem,你可以尝试使用Ruby的原生Struct类和一些Ruby技巧,比如splat运算符 。

 # 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" 

请注意,这个简单的解决方案仅适用于单级哈希。 为了使它对任何类型的Hash都是动态的和完全可用的,你必须使它在你的struct中创建子结构是递归的。

您也可以将Struct存储为类,就像它一样。

 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) 

阅读有关Ruby Struct类的更多信息。

如果它在Rspec中存根也会起作用。

 let(:item) { stub(current: 1, total: 1) }