将 PHP 对象图序列化/反序列化为 JSON

Posted

技术标签:

【中文标题】将 PHP 对象图序列化/反序列化为 JSON【英文标题】:Serialize/unserialize PHP object-graph to JSON 【发布时间】: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 感兴趣以跟踪已序列化的对象 - 因为看起来您只关心对象。 这是真的(并且已知),但是您的序列化应该是一个事务,因此 - 正如您所知道的缺点 - 不应该是编写实现的问题。 【参考方案1】:

完成的脚本(上面发布)符合我的精确要求:

序列化和反序列化整个聚合。

具有与原始数据结构非常相似的 JSON 表示。

不要用动态生成的密钥或其他数据污染数据结构。

它不处理循环引用。正如comment above 中指出的那样,没有正确的方法来存储循环引用或对同一对象的多个引用,因为它们都是相等的。意识到这一点,我决定我的对象图必须是一棵常规树,并接受这个限制作为“一件好事”。

更新:现在可以使用缩进、换行符和空格来格式化输出 - 对我来说,为我的目的提供人类可读(且源代码控制友好)的表示非常重要。 (可以根据需要启用或禁用格式化。)

【讨论】:

【参考方案2】:

我不知道这是否是您所追求的,但如果您只对获取对象的公共属性感兴趣,get_object_vars($obj) 可以解决问题。

<?php

class foo 
    public $fname = "John";
    public $sname = "Doe";
    private $status = null;
    static $type = "person";




$obj = new foo;

print_r( (get_object_vars($obj)) );

print json_encode(get_object_vars($obj));

?>

将输出:

数组([fname] => John [sname] => Doe)

"fname":"John","sname":"Doe"

上面的方法对于访问函数引用和私有变量是没有用的,但是你可以结合一些更多的代码来使用它来敲出你想要的东西。

迪内什。

【讨论】:

如果你查看我发布的实现,我得到的远不止这些......

以上是关于将 PHP 对象图序列化/反序列化为 JSON的主要内容,如果未能解决你的问题,请参考以下文章

怎么将一个数组序列化与反序列化????越详细越好!!!可以加分

将 xml 反序列化为对象时出错:System.FormatException 输入字符串格式不正确

将 JSON 反序列化为匿名对象

将 Objective-C 对象序列化和反序列化为 JSON

将 JSON 反序列化为对象

将 JSON 反序列化为对象