Глубокая конвертация OpenStruct в JSON
У меня есть OpenStruct
это вложено со многими другими OpenStructs
, Как лучше всего конвертировать их все в JSON?
В идеале:
x = OpenStruct.new
x.y = OpenStruct.new
x.y.z = OpenStruct.new
z = 'hello'
x.to_json
// {y: z: 'hello'}
реальность
{ <OpenStruct= ....> }
6 ответов
Не существует методов по умолчанию для выполнения такой задачи, потому что встроенный #to_hash
возвращает представление Hash, но оно не конвертирует значения
Если значение является OpenStruct
возвращается как таковой и не преобразуется в Hash
,
Однако это не так сложно решить. Вы можете создать метод, который обходит каждый ключ / значение в OpenStruct
экземпляр (например, используя each_pair
), рекурсивно спускается во вложенные OpenStruct
s, если значение OpenStruct
и возвращает Hash
только основных типов Ruby.
такие Hash
затем можно легко сериализовать, используя .to_json
или же JSON.dump(hash)
,
Это очень быстрый пример
def openstruct_to_hash(object, hash = {})
object.each_pair do |key, value|
hash[key] = value.is_a?(OpenStruct) ? openstruct_to_hash(value) : value
end
hash
end
openstruct_to_hash(OpenStruct.new(foo: 1, bar: OpenStruct.new(baz: 2)))
# => {:foo=>1, :bar=>{:baz=>2}}
Исправления к вышеуказанному решению для обработки массивов
def open_struct_to_hash(object, hash = {})
object.each_pair do |key, value|
hash[key] = case value
when OpenStruct then open_struct_to_hash(value)
when Array then value.map { |v| open_struct_to_hash(v) }
else value
end
end
hash
end
в
initializers/open_struct.rb
:
require 'ostruct'
# Because @table is a instance variable of OpenStruct and Object#as_json returns Hash of instance variables.
class OpenStruct
def as_json(options = nil)
@table.as_json(options)
end
end
Применение:
OpenStruct.new({ a: { b: 123 } }).as_json
# Result
{
"a" => {
"b" => 123
}
}
Редактировать:
похоже, это почти то же самое (обратите внимание, что ключи - это символы, а не строки)
OpenStruct.new({ a: { b: 123 } }).marshal_dump
# Result
{
:a => {
:b => 123
}
}
Вот еще один подход, измененный на основе ответа lancegatlin. Также добавляем метод в сам класс OpenStruct.
class OpenStruct
def deep_to_h
each_pair.map do |key, value|
[
key,
case value
when OpenStruct then value.deep_to_h
when Array then value.map {|el| el === OpenStruct ? el.deep_to_h : el}
else value
end
]
end.to_h
end
Та же функция, которая может принимать массивы в качестве входных данных
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
Ничего из вышеперечисленного не помогло мне с копированием и вставкой. Добавляем это решение:
require 'json'
class OpenStruct
def deep_to_h
each_pair.map do |key, value|
[
key,
case value
when OpenStruct then value.deep_to_h
when Array then value.map {|el| el.class == OpenStruct ? el.deep_to_h : el}
else value
end
]
end.to_h
end
end
json=<<HERE
{
"string": "fooval",
"string_array": [
"arrayval"
],
"int": 2,
"hash_array": [
{
"string": "barval",
"string2": "bazval"
},
{
"string": "barval2",
"string2": "bazval2"
}
]
}
HERE
os = JSON.parse(json, object_class: OpenStruct)
puts JSON.pretty_generate os.to_h
puts JSON.pretty_generate os.deep_to_h