Deep Convert Openstruct to JSON

Deep Convert OpenStruct to JSON

There is no default methods to accomplish such task because the built-in #to_hash returns the Hash representation but it doesn't deep converts the values.

If a value is an OpenStruct, it's returned as such and it's not converted into an Hash.

However, this is not that complicated to solve. You can create a method that traverses each key/value in an OpenStruct instance (e.g. using each_pair), recursively descends into the nested OpenStructs if the value is an OpenStruct and returns an Hash of just Ruby basic types.

Such Hash can then easily be serialized using either .to_json or JSON.dump(hash).

This is a very quick example, with an update from @Yuval Rimar for arrays of OpenStructs:

def openstruct_to_hash(object, hash = {})
case object
when OpenStruct then
object.each_pair do |key, value|
hash[key] = openstruct_to_hash(value)
end
hash
when Array then
object.map { |v| openstruct_to_hash(v) }
else object
end
end

openstruct_to_hash(OpenStruct.new(foo: 1, bar: OpenStruct.new(baz: 2)))
# => {:foo=>1, :bar=>{:baz=>2}}

How to convert recursive/nested OpenStruct Object to Hash

Check out docs.

You can use OpenStruct#marshal_dump:

openstruct_object.marshal_dump

OpenStruct#to_h will work, too:

openstruct_object.to_h

You can convert your object to hash and then hash to JSON:

openstruct_object.to_h.to_json

But it looks like what you want is a Hash object, not JSON object.

Convert Hash to OpenStruct recursively

personally I use the recursive-open-struct gem - it's then as simple as RecursiveOpenStruct.new(<nested_hash>)

But for the sake of recursion practice, I'll show you a fresh solution:

require 'ostruct'

def to_recursive_ostruct(hash)
result = hash.each_with_object({}) do |(key, val), memo|
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
end
OpenStruct.new(result)
end

puts to_recursive_ostruct(a: { b: 1}).a.b
# => 1

edit

Weihang Jian showed a slight improvement to this here https://stackoverflow.com/a/69311716/2981429

def to_recursive_ostruct(hash)
hash.each_with_object(OpenStruct.new) do |(key, val), memo|
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
end
end

Also see https://stackoverflow.com/a/63264908/2981429 which shows how to handle arrays

note

the reason this is better than the JSON-based solutions is because you can lose some data when you convert to JSON. For example if you convert a Time object to JSON and then parse it, it will be a string. There are many other examples of this:

class Foo; end
JSON.parse({obj: Foo.new}.to_json)["obj"]
# => "#<Foo:0x00007fc8720198b0>"

yeah ... not super useful. You've completely lost your reference to the actual instance.

Construct nested OpenStruct object

This method is rude method but works,

require 'ostruct'
require 'json'
# Data in hash
data = {"names" => {"first_name" => "Bob"}}
result = JSON.parse(data.to_json, object_class: OpenStruct)

And another method is adding method to Hash class itself,

class Hash
def to_openstruct
JSON.parse to_json, object_class: OpenStruct
end
end

Using above method you can convert your hash to openstruct

data = {"names" => {"first_name" => "Bob"}}
data.to_openstruct

Hyphens in serialized JSON OpenStruct

You can use square brackets like you would with a hash:

obj["10-20"]
#=> 0

Of course, if most of the keys are not valid method names anyway, then you might as well just use a hash and not bother with an OpenStruct.

Related documentation: OpenStruct#[]

Ruby: Convert nested hash to object?

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

Ruby: Convert nested hash to object?

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


Related Topics



Leave a reply



Submit