web-dev-qa-db-ja.com

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はできません。どうしてこれなの?

29
Kyle Decot

Hashには、キーのドット構文はありません。 OpenStructは:

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

注:ネストされたハッシュでは機能しません。

58
steenslag

OpenStructは純粋なハッシュに対してはうまく機能しますが、埋め込み配列または他のハッシュを含むハッシュの場合、ドット構文が詰まります。別の宝石を読み込まなくてもうまく機能するこのソリューションに遭遇しました: 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のすべてのオブジェクトにアクセスできます。

21
whodabudda

Rubyハッシュはこのようにネイティブでは機能しませんが、 HashDot gemはこれで機能します。

HashDotでは、ハッシュでドット表記構文を使用できます。また、JSON.parseで再解析されたjson文字列でも機能します。

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
10
steel

これはJavaScriptの機能であり、Ruby機能ではありません。Rubyでは、「ドット構文」を使用するには、オブジェクトがこれらのメソッドに応答する必要があります。Ruby =ハッシュは#[](key)メソッドを使用して要素にアクセスします。

6
d11wtq

なぜ、あなたはメタプログラミングを介してこれを行うことができます

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"
5
megas

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をクラスのように格納することもできます。

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クラスについて詳しく読む

1
Vini Brasil

Rspecにある場合、スタブも機能します。

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

Hashにはversionメソッドがないためです。

0
Jörg W Mittag