【问题标题】:How to convert recursive/nested OpenStruct Object to Hash如何将递归/嵌套 OpenStruct 对象转换为哈希
【发布时间】:2018-08-22 06:36:22
【问题描述】:

我有一个 OpenStruct 对象,需要转换为 JSON 数据。

样本哈希(来自 RSPEC 助手):

def test_order
 {
   "id": 505311428702,
   "email": "test@gmail.com",
   "closed_at": "",
   "discount_codes": {
      "id": 507328175,
      "text": "test"
   }
 }
end

我正在使用以下递归函数:

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

对于 ex to_recursive_ostruct(test_order),将返回:

<OpenStruct id=505311428702, email="test@gmail.com", closed_at="", ...>

转换后,使用 OpenStructObject.marshal_dump

{
:id=>505311428702, :email=>"test@gmail.com", :closed_at=>"", 

discount_codes=>#<OpenStruct id=507328175, text= "test">}
}

OpenStructObject.marshal_dump 在第一级为我提供正确的数据,

我还希望转换嵌套数据。

我真正需要的是:

{:id=>505311428702, :email=>"test@gmail.com", :closed_at=>"", :discount_codes=>{:id=>507328175, :text=> "test"} }

请帮忙,提前谢谢。

【问题讨论】:

  • 我不明白JSON.parse(openstruct_object.to_json) 是如何为您提供输出的。代码错了,输出不是应该的样子
  • Here 是关于您的问题的一些头脑风暴。

标签: ruby-on-rails json ruby openstruct


【解决方案1】:

在@Andrey-Deineko、@owyongsk、@aldrien.h 所做的工作的基础上,这里是一个处理数组的转换器。虽然 OP 并没有特别关注这一点,但其他人可能会觉得它很有帮助。


def openstruct_to_h(object)
   object.to_h.transform_values do |value|
      value.is_a?(OpenStruct) ? openstruct_to_h(value) : value.is_a?(Array) ? value.map{|v| v.is_a?(String) ? v : openstruct_to_h(v) } : value
   end
end

# Example usage

open_struct_object = OpenStruct.new(paramtest: "ok", array_test: [OpenStruct.new(array_one: true), OpenStruct.new(array_two: false, nested_os: OpenStruct.new(works: 'maybe'))], os_test: OpenStruct.new(testy: "yup")) 

openstruct_to_h(open_struct_object)

=> {:paramtest=>"ok", :array_test=>[{:array_one=>true}, {:array_two=>false, :nested_os=>{:works=>"maybe"}}], :os_test=>{:testy=>"yup"}}

【讨论】:

  • 非常感谢您的回复。对于解析来自 api 的响应非常有用。
【解决方案2】:

查看docs

你可以使用OpenStruct#marshal_dump:

openstruct_object.marshal_dump

OpenStruct#to_h 也可以:

openstruct_object.to_h

您可以将对象转换为散列,然后再将散列转换为 JSON:

openstruct_object.to_h.to_json

但看起来你想要的是一个 Hash 对象,而不是 JSON 对象。

【讨论】:

【解决方案3】:

要将您的深度 openstruct 转换为哈希,您可以使用以下方法:

def deep_openstruct_to_hash(object)
  object.each_pair.with_object({}) do |(key, value), hash|
    hash[key] = value.is_a?(OpenStruct) ? deep_openstruct_to_hash(value) : value
  end
end

然后:

openstruct_object = to_recursive_ostruct(test_order)
#=> #<OpenStruct id=505311428702, email="test@gmail.com", closed_at="", discount_codes=#<OpenStruct id=507328175, text="test">>
deep_openstruct_to_hash(openstruct_object)
# {
#   :id=>505311428702,
#   :email=>"test@gmail.com",
#   :closed_at=>"",
#   :discount_codes=>{
#     :id=>507328175,
#     :text=>"test"
#   }
# }

【讨论】:

  • deep_to_h = -&gt;(hash) { hash.to_h.map { |k, v| [k, v.respond_to?(:to_h) ? deep_to_h.(v) : v] }.to_h } 并将其用作deep_to_h.(test_order) 使该方法更通用。
  • @mudasobwa 但是,如果其中一个值是一个数组,它也可能是一个问题,因为[1, 2, 3].to_h #=&gt; TypeError: wrong element type Integer at 0 (expected array)
  • @JohanWentholt 确实如此。然后可能会明确排除数组。
  • @Andrey 如果我有 ]> deep_openstruct_to_hash 不能转换 (discount_codes) 如果值是数组,知道吗?谢谢。
【解决方案4】:

在 Ruby 2.4+ 上,您可以在猴子补丁中将 transform_values 与递归函数一起使用。

class OpenStruct
  def deep_to_h
    to_h.transform_values do |v|
      v.is_a?(OpenStruct) ? v.deep_to_h : v
    end
  end
end

或者如果你不想打补丁

def deep_to_h(obj)
  obj.to_h.transform_values do |v|
    v.is_a?(OpenStruct) ? deep_to_h(v) : v
  end
end

【讨论】:

  • 很好,但它不会处理数组
【解决方案5】:

还要感谢gist: 也可以转换哈希数组。

def recursive_ostruct(object)
  case object
  when Hash
    hash = {}; object.each{|k,v| hash[k] = recursive_ostruct(v)}
    OpenStruct.new(hash)
  when Array
    object.map {|e| recursive_ostruct(e) }
  else
    object
  end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-04
    • 2015-08-12
    • 2013-11-13
    • 1970-01-01
    相关资源
    最近更新 更多