Ответ 1
Hash
не имеет точечного синтаксиса для его ключей. OpenStruct
делает:
require 'ostruct'
hash = {:name => 'John'}
os = OpenStruct.new(hash)
p os.name #=> "John"
Я использую net/http
, чтобы вытащить некоторые json-данные из API Yahoo Placemaker. Получив ответ, я выполняю 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 будет хорошо работать для чистого хеша, но для хэшей с встроенными массивами или другими хэшами синтаксис точек захлестнет. Я наткнулся на это решение, которое хорошо работает без загрузки в другой камень: 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 подойдет для этого.
HashDot позволяет использовать синтаксис точечной нотации для хешей. Он также работает со строками json, которые были повторно проанализированы с помощью 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
Это функция JavaScript, а не функция 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
.
Если вы не хотите устанавливать какие-либо гемы, вы можете попробовать использовать собственный класс 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)
Если это в заглушках Rspec тоже будет работать.
let(:item) { stub(current: 1, total: 1) }