Ruby-on-rails 将哈希转换为对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26809848/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
convert hash to object
提问by hehehuhu
I am trying to convert hash and nested hashes to objects.
我正在尝试将哈希和嵌套哈希转换为对象。
so far first hash object is converted successfully by this code:
到目前为止,第一个哈希对象已通过此代码成功转换:
class Hashit
def initialize(hash)
hash.each do |k,v|
self.instance_variable_set("@#{k}", v)
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
end
end
end
But problem is, i also want to convert nested hash objects. but couldn't make it.
但问题是,我也想转换嵌套的哈希对象。但没能做到。
h = Hashit.new({a: '123r', b: {c: 'sdvs'}})
=> #<Hashit:0x00000006516c78 @a="123r", @b={:c=>"sdvs"}>
see @b={:c=>"sdvs"}this part in output. I want also to convert it to object. is it possible if yes then how?
@b={:c=>"sdvs"}在输出中看到这部分。我也想将它转换为对象。如果是,那有可能吗?
采纳答案by ThomasSevestre
You need to add recursivity:
您需要添加递归性:
class Hashit
def initialize(hash)
hash.each do |k,v|
self.instance_variable_set("@#{k}", v.is_a?(Hash) ? Hashit.new(v) : v)
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
end
end
end
h = Hashit.new({a: '123r', b: {c: 'sdvs'}})
# => #<Hashit:0x007fa6029f4f70 @a="123r", @b=#<Hashit:0x007fa6029f4d18 @c="sdvs">>
回答by Samda
You can use OpenStruct http://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html
您可以使用 OpenStruct http://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html
user = OpenStruct.new({name: "Jimmy Cool", age: "25"})
user.name #Jimmy Cool
user.age #25
回答by Vladimir Chervanev
Another way is to use JSON and OpenStruct, which are standard ruby libs:
另一种方法是使用 JSON 和 OpenStruct,它们是标准的 ruby 库:
irb:
> require 'JSON'
=> true
> r = JSON.parse({a: { b: { c: 1 }}}.to_json, object_class: OpenStruct)
=> #<OpenStruct a=#<OpenStruct b=#<OpenStruct c=1>>>
> r.a.b.c
=> 1
回答by Abhilash Reddy
Ruby has an inbuilt data structure OpenStruct to solve something like this. Still, there is a problem. It is not recursive. So, you can extend OpenStruct class like this:
Ruby 有一个内置的数据结构 OpenStruct 来解决这样的问题。尽管如此,还是有问题。它不是递归的。因此,您可以像这样扩展 OpenStruct 类:
# Keep this in lib/open_struct.rb
class OpenStruct
def initialize(hash = nil)
@table = {}
if hash
hash.each_pair do |k, v|
k = k.to_sym
@table[k] = v.is_a?(Hash) ? OpenStruct.new(v) : v
end
end
end
def method_missing(mid, *args) # :nodoc:
len = args.length
if mname = mid[/.*(?==\z)/m]
if len != 1
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
end
modifiable?[new_ostruct_member!(mname)] = args[0].is_a?(Hash) ? OpenStruct.new(args[0]) : args[0]
elsif len == 0 # and /\A[a-z_]\w*\z/ =~ mid #
if @table.key?(mid)
new_ostruct_member!(mid) unless frozen?
@table[mid]
end
else
begin
super
rescue NoMethodError => err
err.backtrace.shift
raise
end
end
end
end
and remember to require 'open_struct.rb'next time you want to use OpenStruct.
require 'open_struct.rb'下次要使用 OpenStruct 时请记住。
Now you can do something like this:
现在你可以做这样的事情:
person = OpenStruct.new
person.name = "John Smith"
person.age = 70
person.more_info = {interests: ['singing', 'dancing'], tech_skills: ['Ruby', 'C++']}
puts person.more_info.interests
puts person.more_info.tech_skills
回答by Ben
You could check the type on vwhen you initialize the object and call newto get a new Hashitwhen it is a another hash.
您可以v在初始化对象时检查类型,并在它是另一个散列时调用new以获取新对象Hashit。
class Hashit
def initialize(hash)
hash.each do |k,v|
self.instance_variable_set("@#{k}", v.is_a?(Hash) ? Hashit.new(v) : v)
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
end
end
end
and the resulting snippet from before would be:
之前的结果片段将是:
h = Hashit.new({a: '123r', b: {c: 'sdvs'}})
=> #<Hashit:0x007fa71421a850 @a="123r", @b=#<Hashit:0x007fa71421a5a8 @c="sdvs">>
回答by Cary Swoveland
If I understand the question correctly, this should do it:
如果我正确理解了这个问题,应该这样做:
class Hashit
def initialize(hash)
convert_to_obj(hash)
end
private
def convert_to_obj(h)
h.each do |k,v|
self.class.send(:attr_accessor, k)
instance_variable_set("@#{k}", v)
convert_to_obj(v) if v.is_a? Hash
end
end
end
h = Hashit.new( { a: '123r',
b: { c: 'sdvs', d: { e: { f: 'cat' }, g: {h: 'dog'} } } })
#=> #<Hashit:0x000001018eee58 @a="123r",
# @b={:c=>"sdvs", :d=>{:e=>{:f=>"cat"}, :g=>{:h=>"dog"}}},
# @c="sdvs", @d={:e=>{:f=>"cat"}, :g=>{:h=>"dog"}},
# @e={:f=>"cat"}, @f="cat", @g={:h=>"dog"}, @h="dog">
h.instance_variables
#=> [:@a, :@b, :@c, :@d, :@e, :@f, :@g, :@h]
Hashit.instance_methods(false)
#=> [:a, :a=, :b, :b=, :c, :c=, :d, :d=, :e, :e=, :f, :f=, :g, :g=, :h, :h=]
h.d
#=> {:e=>{:f=>"cat"}}
h.d = "cat"
h.d
#=> "cat"

