为什么编组可以序列化循环引用列表而json不能?

时间:2017-01-03 13:51:50

标签: json ruby serialization marshalling circular-reference

这里我有一个循环引用列表

2.1.9 :082 > a = []
 => [] 
2.1.9 :083 > a.append(a)
 => [[...]] 

当尝试将a转储为json时,我收到错误

a.to_json
ActiveSupport::JSON::Encoding::CircularReferenceError: object references itself

但是当我尝试Marshal他们时,我得到一个有效的字符串

2.1.9 :085 > Marshal.dump(a)
 => "\x04\b[\x06@\x00" 

我只是试图确保他们通过再次加载来正确地转储价值

 b = Marshal.load("\x04\b[\x06@\x00")
 => [[...]] 

以下是一些更多验证,以确保它们正确地将对象转储到字符串

2.1.9 :088 > a.object_id
 => 70257482733700 
2.1.9 :089 > a.first.object_id
 => 70257482733700 
2.1.9 :090 > b.object_id
 => 70257501553000 
2.1.9 :091 > b.first.object_id
 => 70257501553000 
2.1.9 :092 > 

据我所知,他们都将对象转换为字符串并从字符串中取回对象。我还能看到json没有任何构造来引用json的其他部分,这可能是它不能支持这种操作的原因。但是,在json中引入这样的结构是否很难以促进当前的情况。我可能会遗漏一些关于编组和序列化的更重要的事情,请赐教。

2 个答案:

答案 0 :(得分:3)

  

在我的理解中,他们都将对象转换为字符串并从字符串中取回对象。

是。这几乎是"序列化"的定义。或"编组"。

  

我还能看到json没有任何构造来引用json的其他部分,这可能是它无法支持这种操作的原因。

是的,这就是原因。

  

但是在json中引入这样的结构是否难以促进目前的情况。

您无法在JSON中介绍构造。它被故意设计为没有版本号,因此它永远不会被改变。

当然,这只意味着我们现在无法添加 ,但是Doug Crockford可以从一开始就将添加到,当他设计JSON时?当然是。但他没有。 JSON was deliberately designed to be simple (bold emphasis mine)

  

JSON不是文档格式。它不是标记语言。它甚至不是一般的序列化格式,因为它没有直接表示循环结构 [...]

例如,参见YAML,它是JSON的超集,它具有引用,因此可以表示循环数据。

答案 1 :(得分:1)

Marshal.dumpto_json都返回一个字符串,但这是关于他们共同拥有的所有内容。

to_json

to_json根据JSON specifications返回描述Ruby对象的String。

基本上每个可能的Ruby对象都需要对

to_json进行修补,当在一个数组上调用时,它在每个元素上都被称为recursively

"[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]"

这种递归是你得到的原因:

ActiveSupport::JSON::Encoding::CircularReferenceError: object references itself

如果导出成功,则可以通过新的Rubinius脚本读取在旧的JRuby on Rails服务器或PHP服务器上编写的JSON字符串。

转储

Marshal.dump返回一个字节流,表示对象本身,以及Ruby如何在内部存储它:

Marshal.dump(a).bytes
#=> [4, 8, 91, 6, 64, 0]
Marshal.dump([[]]).bytes
#=> [4, 8, 91, 6, 91, 0]
Marshal.dump([]).bytes
#=> [4, 8, 91, 0]

因此Marshal.dump存储a已定义:一个元素数组,引用自身。

前两个字节是主要版本号和次要版本号。将转储对象与相同版本进行比较时,可以使用以下命令忽略它们:

Marshal.dump(a).bytes.drop(2)
#=> [91, 6, 64, 0]
Marshal.dump([[]]).bytes.drop(2)
#=> [91, 6, 91, 0]

由于表示依赖于Ruby实现,因此从一个Ruby脚本转储到另一个Ruby脚本可能并不总是有效。

来自doc

  

在正常使用中,编组只能加载用相同的数据写入的数据   主要版本号和相等或较低的次要版本号。