将JSON反序列化为结构体?

3
如何将一个 JSON 字符串反序列化为 Struct 而不是默认的 Hash(即一个哈希表,其中哈希键可以使用类似 attr_accessor 的方法在对象上访问),换句话说,我真的想能够编写 obj.my_json_property 来访问数据。
3个回答

4
如果接受使用OpenStruct(仍然可以将结构体用作obj.my_json_property,即满足原始要求),请使用以下方法:
require 'json'
obj = JSON.parse(json_text, object_class: OpenStruct)
puts obj.my_json_property

我尝试使用object_class: Struct,但是它会出现"TypeError: allocator undefined for Struct"的错误。然而,对于大多数(但不是全部)实际目的而言,OpenStruct完全可以胜任。其主要缺点是,如果您拼错了某个属性名称,则您的代码仍将正常运行,该值将只是nil/空字符串(具体取决于上下文)......这可能有点令人恼火和误导。
这有点像Hash#[]Hash#fetch之间的区别(有关参考,请参见:http://devblog.avdi.org/2009/03/16/go-fetch/)。

但是你为什么要这样做呢?Hash在数据访问方面比OpenStruct快得多。 - engineersmnky
有时候,速度并不是最重要的目标。实际上,如果是的话,为什么一开始就要用Ruby编写代码呢?;)我喜欢真实的对象;这样代码更清晰、更美丽。 - Per Lundberg
一个Hash是一个真正的对象,但每个人都有自己的喜好。如果你真的想要一个Struct,不妨试试我的答案。 - engineersmnky
通过“真实对象”,我指的是具有方法的东西... :) 而不是必须使用Hash#[]Hash#fetch来检索内容。 - Per Lundberg

1
这是将一个Hash转换为Struct的相对快速的方法:
 class Hash
    def to_struct
        s = Struct.new(*self.keys.map(&:to_sym))
        construct = map do |k,v|
            v.is_a?(Hash) ? v.to_struct : v.is_a?(Array) ? v.join(", ") : v
        end
        s.new(*construct)
    end
 end

然后您可以做一些事情,例如:


h = {hello: 'world',foo: {bar: 'baz'}}
m = h.to_struct
#=> #<struct hello="world", foo=#<struct bar="baz">>
m.hello
#=> "world"
m.foo
#=> #<struct bar="baz">
m.foo.bar
#=> "baz"

在您的情况下:

require 'json'
obj = JSON.parse(json_text).to_struct

这将能够正常工作并且也能处理嵌套对象(需要一些明显的处理,针对 Array *我没有关注这部分,因为它不是问题的一部分)。


0
使用Struct.new创建一个具有所需访问器的新类,您可以将其作为object_class传递给JSON.parse
> require 'json'
> MyStruct = Struct.new(:my_json_property)
> obj = JSON.parse('{"my_json_property": "foo"}', object_class: MyStruct)
=> #<struct  my_json_property="foo">
> obj.my_json_property
=> "foo"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接