【问题标题】:Overriding getter of OpenStruct in order to print it as a Hash覆盖 OpenStruct 的 getter 以便将其打印为 Hash
【发布时间】:2015-10-28 18:10:49
【问题描述】:

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

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

MyOpenStruct 覆盖 newto_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

但是覆盖[] 并没有任何区别。例如

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

仅当我使用[] 访问密钥时,才会返回哈希!!

我该如何纠正这个问题??

【问题讨论】:

  • 您的问题不清楚。你的目标是什么?您期望的输出是什么,而您得到的输出是什么?
  • 我希望 temp.place 返回一个哈希而不是 MyOpenConstruct 对象。压倒一切没有帮助。输入哈希可以比给定的更深。每个访问都应该返回一个哈希
  • 如果您将其作为哈希返回,为什么要创建嵌套的 OpenStruct?
  • @Doguita 我如何获得对值的点访问?我的意思是temp.nametemp[:name] 不会相同。截至目前,两者都在工作。它只是我需要两者的哈希值。 temp.name 返回一个对象,但 temp[:name] 返回一个哈希
  • 如果你想让place返回不同的东西,那么你需要在你的子类中覆盖它。 name 也是如此。

标签: ruby openstruct


【解决方案1】:

如果您将其作为哈希返回,我认为创建嵌套的 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" 

【讨论】:

  • @AnimeshPandey Doguita 在前两段中解释了为什么会发生这种情况。回去阅读它们。如果struct.place 返回一个Hash,你不能做struct.place.animal,因为Hash 不会有animal 方法。没有办法解决这个问题。
【解决方案2】:

@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

【讨论】:

  • 是的,@Doguita 的回答似乎是正确的,但 struct.place.animal 提出了 NoMethodError !!!
  • @AnimeshPandey @Doguita 在前两段中解释了为什么会发生这种情况。回去阅读它们。如果struct.place 返回一个Hash,你不能做struct.place.animal,因为Hash 不会有animal 方法。没有办法解决这个问题。
  • 是的,我明白了。他也是对的。就是这样!!
  • 答案对我来说是一个很好的信息来源。我认为到目前为止,我对所提出的任何建议都很满意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-20
  • 2013-08-07
  • 2021-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多