convert hash to object

后端 未结 6 709
小蘑菇
小蘑菇 2021-02-07 03:06

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(         


        
相关标签:
6条回答
  • 2021-02-07 03:30

    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:

    # 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.

    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
    
    0 讨论(0)
  • 2021-02-07 03:43

    Another way is to use JSON and OpenStruct, which are standard ruby libs:

    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
    
    0 讨论(0)
  • 2021-02-07 03:46

    You could check the type on v when you initialize the object and call new to get a new Hashit when it is a another hash.

    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">>
    
    0 讨论(0)
  • 2021-02-07 03:47

    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"
    
    0 讨论(0)
  • 2021-02-07 03:48

    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">>
    
    0 讨论(0)
  • 2021-02-07 03:50

    You can use 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
    
    0 讨论(0)
提交回复
热议问题