【发布时间】:2012-05-16 10:06:15
【问题描述】:
我想将完整的 PHP 对象图序列化为 JSON 字符串表示,然后将其反序列化回相同的 PHP 对象图。
以下是我考虑过的选项的摘要,以及它们不适合我的原因:
serialize()不符合我的要求,因为它使用特定于 PHP 的格式。我想要一种被大多数语言广泛支持并且人类可读/可编辑的格式。json_encode()没有做我想做的事,因为它只做简单的值和数组,而不是对象。 (我实际上在我的实现中使用了它,见下文。)var_export()不处理循环引用,也不做我想做的事(见上文。)(请注意,我当前的实现也不处理循环引用 - 请参阅 cmets 并在下面回复为了澄清这个问题。)Sebastian Bergmann 的 Object Freezer 是一个不错的实现,但它也没有达到我想要的效果 - 它使用很长的形式,并且依赖于使用 GUID 填充序列化对象。
Serialized 没有做我想要的 - 它实际上并没有执行序列化,它解析
serialize()的输出并产生不同的表示,例如XML,但无法解析该表示。 (它也不支持 JSON - XML 格式很长,不是我想要的。)
我现在有一个工作实现要分享:
https://github.com/mindplay-dk/jsonfreeze
对象图的 JSON 表示如下所示:
{
"#type": "Order",
"orderNo": 123,
"lines": [{
"#type": "OrderLine",
"item": "milk \"fuzz\"",
"amount": 3,
"options": null
}, {
"#type": "OrderLine",
"item": "cookies",
"amount": 7,
"options": {
"#type": "#hash",
"flavor": "chocolate",
"weight": "1\/2 lb"
}
}],
"paid": true
}
此方法旨在用于纯树结构聚合 - 不允许循环引用,也不允许对同一对象进行多次引用。换句话说,这不是通用的,例如serialize() 和 unserialize() 哪个函数适用于任何 PHP 对象图。
在我的initial approach 中,我使用了一个序列化表单,它本质上是一个以 0 为基数的对象列表。列表中的第一个对象(编号 0)是序列化对象图的根,任何其他对象都按照它们被发现的顺序存储。
在当前的实现中,JSON 表示类似于原始的树结构,以扩展这是可能的,这使得在 JavaScript 中实际使用对象图的 JSON 表示成为可能。唯一的偏差是神奇的#type 属性(以# 为前缀以防止与属性名称冲突)和#hash“类型”,用于区分array 类型的哈希(存储为JSON 对象)和常规@987654337 @-type 数组(存储为 JSON 数组)。
出于历史目的,我将在此处留下有关先前版本的这些注释。
循环引用通过从不将嵌套对象存储在每个对象的序列化表示中来简单地处理 - 相反,任何对象引用都存储为带有对象索引的 JSON 对象 - 例如{"__oref":2} 是对对象列表中索引为 2 的对象的引用。
我在实现中遇到了数组引用的问题 - 当我在代码中使用 var_dump() 恢复对数组的对象的引用时,它们正在被填充,但在某些时候数组被复制了,而你以空副本结束。我尝试在代码中的任何位置放置 & 字符,但无论我通过引用传递到哪里,最终结果都是一个空数组。
【问题讨论】:
-
我不会将 PHP 的序列化格式称为专有的,因为它已记录在案 - at least inside the source-code - 但第三方也是如此。所以你可以使用它。
-
它可能会被记录,但 PHP 是唯一具有 unserialize() 实现的平台/语言 - 我更喜欢 JSON,它已经被所有主流语言支持。而且是人类可读的。
-
它支持PHP中序列化所支持的,即递归和递归引用。格式是分层的 - 不像你的那样平坦。正如所写,PHP 的序列化支持对象等复杂类型,并且可以嵌套。
-
PHP 序列化表单的做法与此类似,在分层信息旁边,它还跟踪每个值的数字索引,因此可以引用它——如果有引用的话。这还将注意第一个原生 PHP 引用是序列化形式中的实际值,并且每个下一个引用/别名都将引用到数字位置。对于您的任务,您可能对
spl_object_hash感兴趣以跟踪已序列化的对象 - 因为看起来您只关心对象。 -
这是真的(并且已知),但是您的序列化应该是一个事务,因此 - 正如您所知道的缺点 - 不应该是编写实现的问题。
标签: php arrays json serialization reference