如何使用JSON进行Ruby对象序列化
我有一个像这样的简单容器类的结构(在伪ruby中):
class A attr_reader :string_field1, :string_field2 ... end class B attr_reader: int_field3, :string_field4 ... end # C includes an instance of A and B class C attr_reader: :a_instance, :b_instance ... end
是否有简单的方法在Ruby中将其/序列化为JSON? 或者我应该为每个类制作嵌套的序列化方法,如本例所示 ?
编辑:
在我的特定场景中,我想将一些JSON数据发送到运行Ruby的服务器,这将提取数据并采取相应的行动。
JSON的发送者不一定是Ruby进程,但可能是其他系统的后端。 (虽然在我的测试工具中是Ruby)。
因此,我不需要JSON采用某种“特定于Ruby”的格式,我只是假设如果它实际上是内置于Ruby的话会更容易。
使用Marshal,PStore或其他Ruby解决方案来处理非JSON对象
有人可能有理由怀疑为什么像Ruby这样的完全reflection的语言不会自动化JSON生成和任意类的解析。
但是,除非您坚持使用JSON类型,否则除了运行另一个Ruby之外,没有地方可以发送或接收JSON对象。 在这种情况下,我怀疑传统的智慧是“忘记JSON,使用像核心类Marshal
一样的原生Ruby接口。
因此,如果您真的将这些对象发送到PHP或非Ruby的东西,那么您应该使用Array等直接创建支持JSON的Ruby数据结构,然后您将拥有JSON.generate将直接处理的内容。
如果你只是需要序列化,那么你应该使用Marshal
或PStore
。
更新:啊哈,好的,试试这个:
module AutoJ def auto_j h = {} instance_variables.each do |e| o = instance_variable_get e.to_sym h[e[1..-1]] = (o.respond_to? :auto_j) ? o.auto_j : o; end h end def to_json *a auto_j.to_json *a end end
如果你在每个类中都include AutoJ
,它应该是DTRT。 在您的示例中,这会导致
{"a":{"string_field1":"aa","string_field2":"bb"},"b":{"int_field3":123,"string_field4":"dd"}}
您可能希望更改auto_j
方法以返回h.values
而不是h
,在这种情况下,您将获得:
[["aa","bb"],[123,"dd"]]
我有同样的问题(主要是尝试创建任意复杂的JSON字符串)而不是解析它们。 在查看了一个非侵入性类,它将采用Ruby对象(包括嵌套数组)并将其编组为JSON字符串后,我终于编写了自己的简单序列化器。 此代码还会转义特殊字符以创建有效的JSON。
http://www.keepingmyhandin.com/Downhome/Sketchup/simplejsonserializerrubyimplementation
你所要做的就是:
json = JSON.new; jsonString = json.marshal(obj); # Where obj is a Ruby object
这是我对自定义类的to_json
实现的方法。
使用self.included
在模块中有一点魔力。 这是2006年关于模块同时具有实例和类方法的非常好的文章http://blog.jayfields.com/2006/12/ruby-instance-and-class-methods-from.html
该模块旨在包含在任何类中以提供to_json
function。 它拦截attr_accessor
方法而不是使用它自己的方法,以便对现有类进行最小的更改。
to_json
实现基于这个答案
module JSONable module ClassMethods attr_accessor :attributes def attr_accessor *attrs self.attributes = Array attrs super end end def self.included(base) base.extend(ClassMethods) end def as_json options = {} serialized = Hash.new self.class.attributes.each do |attribute| serialized[attribute] = self.public_send attribute end serialized end def to_json *a as_json.to_json *a end end class CustomClass include JSONable attr_accessor :b, :c def initialize b: nil, c: nil self.b, self.c = b, c end end a = CustomClass.new(b: "q", c: 23) puts JSON.pretty_generate a { "b": "q", "c": 23 }