Ruby 使用 JSON 序列化结构

Ruby serialize struct with JSON

我正在尝试将一个简单的结构序列化为 JSON,这工作正常,但我无法让它从 JSON 创建该结构的实例。这是我正在尝试的方法。

require 'rubygems'
require 'json'

Person = Struct.new(:name, :age)

json = Person.new('Adam', 19).to_json
puts json

me = JSON.load(json)
puts me.name

我得到以下输出:

"#<struct Person name=\"Adam\", age=19>"
/usr/lib/ruby/1.9.1/json/common.rb:148:in `parse': 746: unexpected token at '"#<struct Person name=\"Adam\", age=19>"' (JSON::ParserError)
    from /usr/lib/ruby/1.9.1/json/common.rb:148:in `parse'
    from /usr/lib/ruby/1.9.1/json/common.rb:309:in `load'
    from why.rb:9:in `<main>'

在这种情况下,person.to_json 没有按照您的预期进行。

当您 require 'json' 时,JSON 库会在 Object 上插入一个 #to_json 方法,如果其他地方没有提供专门的 #to_json 方法,这是一个后备方法. This inserted method 与在对象上调用 #to_s#to_json 基本相同。

对于此处的 Person class,#to_s 输出 standard Object#to_s,默认情况下,它不提供可由 JSON图书馆。

但是,Struct 提供了一个 #to_h 方法,可用于将该结构转换为 Hash,并且 Hash 是(在需要 JSON 库时)知道如何生成 JSON 可解析的输出。

所以简单地改变:

json = Person.new('Adam', 19).to_json
puts json

至:

person = Person.new('Adam', 19)
puts person.to_h.to_json

会如您所愿。

(顺便说一句,我实际上建议直接在 Person class 上实现 #to_json,因为调用 #to_h#to_json 违反了 Law of Demeter。)

您还可以使用 to_json 方法定义结构。取决于您是否满意致电 to_h.to_json。如果只在一个class内部调用一次,可能还可以忍受,忽略这个。但是如果结构在整个系统中使用,下面是结构上的一个方便的辅助方法。

require 'struct'
require 'json'

MyStruct = Struct.new(:foo, :bar) do
  def to_json
    to_h.to_json
  end
end

simple_struct = MyStruct.new("hello", "world")
simple_struct.to_json

# => "{\"foo\":\"hello\",\"bar\":\"world\"}"