【问题标题】:Serializing an object to JSON, XML, YAML?将对象序列化为 JSON、XML、YAML?
【发布时间】:2011-12-16 11:33:02
【问题描述】:

我之前问过一个关于序列化和验证的问题。有人提到使用 JSON gem,它允许我告诉我的对象如何使用 to_json 方法进行序列化,但是 Ruby 似乎很容易做很多复杂的事情,但另一方面,一些非常简单的事情似乎很复杂,序列化就是其中之一。

我想知道是否有办法拥有一个干净的对象:

class CleanClass
    attr_accessor :variable1
    attr_accessor :variable2
    attr_accessor :variable3
end

cleanObject = CleanClass.new

理想情况下,我不想弄脏模型,我只想将它传递给 something 并告诉它输出类型应该是什么,让它发挥它的魔力。

一个例子是这样的:

jsonOutput = MagicSerializer::Json.Serialize(cleanObject)
xmlOutput = MagicSerializer::Xml.Serialize(cleanObject)
yamlOutput = MagicSerializer::Yaml.Serialize(cleanObject)

revertedJsonObject = MagicSerializer::Json.Unserialize(jsonOutput)
revertedXmlObject = MagicSerializer::Xml.Unserialize(xmlOutput)
revertedYamlObject = MagicSerializer::Yaml.Unserialize(yamlOutput)

我想传递一个对象,并得到输出的字符串,然后能够将其转换回来。

我知道 ActiveModel 具有序列化功能,但我需要弄脏我的班级才能做到这一点,而且我不想尽可能更改模型。 ActiveSupport 似乎满足 JSON 标准,因为我可以调用它,它会接受一个对象并吐出 JSON,但我想支持其他类型。

任何进一步的信息都会很棒!

【问题讨论】:

  • JSON.dump(object)JSON.parse(string)

标签: ruby xml json serialization yaml


【解决方案1】:

Ruby 内置了对二进制和 yaml 的自动序列化/反序列化。

Yaml:

require 'yaml'
serialized = CleanClass.new.to_yaml
object = YAML.load(serialized)

元帅:

serialized = Marshal.dump(CleanClass.new)
object = Marshal.load(serialized)

【讨论】:

  • 是的,我已经看到了这些,但是 Json 目前对我来说是最重要的,然后是 Xml,然后是 Yaml。所以我希望有一个 gem 或库可以在简化的界面上完成所有工作。
  • JSON 格式过于有限,无法捕获整个 ruby​​ 对象的状态。它只有原语,因此您需要自定义 JSON 以包含实例变量的类型信息。此外,如果目标是序列化以便可以在其他地方读回对象,则您可能需要遵守 API。你可以说一个类的职责是将自己转换为外部格式,然后再读回该格式。
  • 就像我在上面对 Dave 的回复中所说的那样,我不明白为什么你需要告诉 ruby​​ 如何序列化这些类(使用上面的假设)。他们真正需要的是一种递归方法来遍历对象的每个实例,直到它们到达另一个对象,然后再深入到树中,我相信所有对象最终都会成为简单类型的模型。唯一一次我可以看到用户需要自己输入任何序列化逻辑是如果他们想更改键的名称,即 attr_accessor :Surname 要序列化为 lastname 或类似的东西。
  • 首先,您不希望只序列化“公共”实例变量。如果需要重新构造对象,还需要私有变量。其次,没有公共实例变量之类的东西。 attr_accessor 实际上只是为始终私有的实例变量定义了一个 getter 和一个 setter。你想要的可以完成,但这不是微不足道的,我不明白你为什么需要它。如果您不关心序列化格式,只需使用 YAML 进行可移植输出或 Marshal。如果您确实关心,请使用 ActiveModel 之类的东西。
  • 对不起,如果我是 vauge,我想坚持一个标准,无论是 Json、Yaml、Xml。但是,我希望将与其中任何一个无关的对象转换为所述格式并从所述格式返回。我认为 Ruby 仍然有公共和私有的概念,只是表达方式不同,无论哪种方式我都会改写,我会假设只有变量被序列化,方法和常量不需要。不是每个 ruby​​ 对象都有一个 instance_variables 方法,它为您提供每个变量的数组,使用该数组您可以使用 instance_variable_get/instance_variable_set 进行序列化和反序列化。
【解决方案2】:

让你的魔法序列化方法为你弄脏对象; emdirtering 可以在每个对象的基础上发生。

或者在你的班级水平上很脏。

不管怎样,你的主线代码都看不到它。

【讨论】:

  • 您能否详细说明一下,我知道您可以在声明后添加到类中,这可能就是您的意思。但是,即使我在该类之外编写了一些代码来告诉它如何序列化,这也是我必须在我的项目中维护的更多代码,并且必须按类类型编写。即,如果一个 Person 类有名字和姓氏,那么一个业务类有名字、地址字段,那么您将需要编写 2 个单独的序列化逻辑来处理它们,对于复杂的对象图,这将开始变得不可维护。跨度>
  • 现有的序列化代码没有被告知如何写出一个对象,至少对于简单的属性,它询问对象。如果你有复杂的序列化需求,你必须告诉它 /somewhere/.你基本上要求两件事:我想要完全控制我的类如何序列化,但我不希望我的类中的信息,我希望它自动处理。它不是那样工作的——你需要定义它将如何被序列化/somewhere/。
  • 让我澄清一下,我并不是要求完全控制我的课程是如何序列化的。我只希望它序列化,可以对任何自动序列化的类做出以下假设,而无需用户付出任何努力。只有可公开访问的字段/变量被序列化,并且它们被用作键值对,除非该值是另一个类,在其中您使用递归继续沿着树向下移动,直到所有简单类型都被序列化。我认为它们是公平的假设,并且(虽然我不太了解 ruby​​)应该很容易访问。
  • 当你的对象有一个对自身的引用作为一个属性,或者对另一个对象的引用又包含一个对你的对象的引用作为它的属性之一时会发生什么?或任何其他类型的循环引用。
  • 那么你不应该序列化它......只是开玩笑,让我澄清一下,我不反对人们手动配置如何序列化他们的对象的某些方面。我只是认为即使不是所有常见场景都可以自动序列化,但还是有很多。我不想将其他语言引入其中,但是来自 .net 我可以轻松地将模型序列化为 json、xml、二进制或几乎任何其他格式,Java 几乎相同,我确信 Python 能够做到我说的是那种东西,但我认为他们称之为酸洗。我只是觉得99%的序列化需求都很简单
【解决方案3】:

对于 JSON 和 YAML,这似乎很容易,因为它们只是 to_yamlto_json 方法(或分别为 YAML.loadfrom_json)的包装器

对于 JSON,您必须围绕核心类型(或实现 to_json 的其他类型)包装自己的类,例如首先实现一个 to_hash 方法,然后可以将其转换为 json。

由于层次结构,XML 要复杂得多,您必须对其进行标准化, 但实际上我从你的解释中不明白基本的to_... 方法有什么问题。这实际上是我们在 Ruby 中使用的约定。如果您担心模型的污染,您可以将这些方法放在一个模块中,并将该模块包含在类中。

module Foo
  def to_serialized_type
   ...
  end
end
class CleanClass
  include Foo
end

【讨论】:

  • 当您尝试将 to_xxxx 与您自己的一个类的实例一起使用时,您必须在您的类中编写一个 to_xxxx 方法来告诉它如何序列化(根据我目前阅读的内容,或者像 # 而不是 {something:blah})。这就是我不想用序列化问题污染模型的问题。
  • 我不会称之为污染,因为这是您在 ruby​​ 中正常工作的一种方式:直接在对象上而不是在功能上
猜你喜欢
  • 1970-01-01
  • 2011-05-06
  • 1970-01-01
  • 1970-01-01
  • 2010-10-08
相关资源
最近更新 更多