将哈希转换为对象

我试图将哈希和嵌套哈希转换为对象。

到目前为止,第一个哈希对象由此代码成功转换:

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 

但问题是,我还想转换嵌套的哈希对象。 但无法成功。

 h = Hashit.new({a: '123r', b: {c: 'sdvs'}}) => #"sdvs"}> 

在输出中看到@b={:c=>"sdvs"}这部分。 我也想将它转换为对象。 有可能,如果是,那么如何?

你需要添加递归:

 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'}}) # => #> 

您可以使用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 

另一种方法是使用JSON和OpenStruct,它们是标准的ruby库:

 irb: > require 'JSON' => true > r = JSON.parse({a: { b: { c: 1 }}}.to_json, object_class: OpenStruct) => #>> > rabc => 1 

您可以在初始化对象时检查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 

而之前产生的片段将是:

 h = Hashit.new({a: '123r', b: {c: 'sdvs'}}) => #> 

如果我正确理解了这个问题,应该这样做:

 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'} } } }) #=> #"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=] hd #=> {:e=>{:f=>"cat"}} hd = "cat" hd #=> "cat" 

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 

并且记得下次要使用OpenStruct时require 'open_struct.rb'

现在你可以这样做:

 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