覆盖OpenStruct的getter以将其打印为Hash

时间:2015-10-28 18:10:49

标签: ruby openstruct

目标:OpenStruct对象的值应打印为哈希而不是对象

可能的解决方案:覆盖OpenStruct类的getter

MyOpenStruct会覆盖new的{​​{1}},to_h[]

OpenStruct

但重写class MyOpenStruct < OpenStruct def initialize(object=nil) @table = {} @hash_table = {} if object object.each do |k,v| if v.is_a?(Array) other = Array.new() v.each { |e| other.push(self.class.new(entry)) } v = other end @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v) @hash_table[k.to_sym] = v new_ostruct_member(k) end end end def [](val) @hash_table[val.to_sym] end end 没有任何区别。 E.g。

[]

只有当我使用irb(main):007:0> temp = MyOpenStruct.new({"name"=>"first", "place"=>{"animal"=>"thing"}}) => #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">> irb(main):008:0> temp.name => "first" irb(main):009:0> temp.place => #<MyOpenStruct animal="thing"> irb(main):010:0> temp["place"] => {"animal"=>"thing"} irb(main):011:0> temp[:place] => {"animal"=>"thing"} irb(main):012:0> temp => #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">> 访问密钥时,才会返回哈希值!!

我该如何纠正这个?

2 个答案:

答案 0 :(得分:2)

如果你把它作为Hash返回,我不相信创建一个嵌套的OpenStruct是有意义的。这就是OpenStruct的工作方式:

require 'ostruct'
struct = OpenStruct.new(name: 'first', place: { animal: 'thing' })
struct.place
# => {:animal=>"thing"}
struct.place[:animal]
# => "thing"
struct.place.animal
# => NoMethodError: undefined method `animal' for {:animal=>"thing"}:Hash

因此,如果要使用点表示法来获取struct.place.animal,则需要像创建嵌套的OpenStruct对象一样创建。 但是,正如我所说,你不需要覆盖[]方法。使用你的类而不覆盖[]我得到了这个:

struct = MyOpenStruct.new(name: 'first', place: { animal: 'thing' })
# => #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">> 
struct.place
# => #<MyOpenStruct animal="thing"> 
struct.place.animal
# => "thing" 

无论如何,如果你真的想按照你的要求使点符号工作,你可以覆盖new_ostruct_member方法,该方法在设置OpenStruct对象时在内部用于创建动态属性。 您可以尝试这样的事情,但我不推荐

class MyOpenStruct < OpenStruct
  def initialize(object=nil)
    @table = {}
    @hash_table = {}

    if object
      object.each do |k,v|
        if v.is_a?(Array)
          other = Array.new()
          v.each { |e| other.push(self.class.new(entry)) }
          v = other
        end
        @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
        @hash_table[k.to_sym] = v
        new_ostruct_member(k)
      end
    end
  end

  def [](val)
    @hash_table[val.to_sym]
  end

  protected

  def new_ostruct_member(name)
    name = name.to_sym
    unless respond_to?(name)
      # use the overrided `[]` method, to return a hash
      define_singleton_method(name) { self[name] }
      define_singleton_method("#{name}=") { |x| modifiable[name] = x }
    end
    name
  end
end

struct = MyOpenStruct.new(name: 'first', place: { animal: 'thing' })
# => #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">> 
struct.place
# => {:animal=>"thing"} 
struct.place[:animal]
# => "thing" 

答案 1 :(得分:1)

@Doguita的答案在各方面都是正确的。我只是想回答你的问题“如果那不可能那么我可以打印整个对象temp的哈希吗?”是的你可以。您只需要覆盖to_h以递归方式遍历您的键和值,并将MyOpenStruct的实例转换为哈希:

def to_h
  @table.each_with_object({}) do |(key, val), table|
    table[key] = to_h_convert(val)
  end
end

private
def to_h_convert(val)
  case val
  when self.class
    val.to_h
  when Hash
    val.each_with_object({}) do |(key, val), hsh|
      hsh[key] = to_h_convert(val)
    end
  when Array
    val.map {|item| to_h_convert(item) }
  else
    val
  end
end