Serialization 使用MongoDB ODM实现文档序列化

Serialization 使用MongoDB ODM实现文档序列化,serialization,zend-framework2,doctrine-odm,doctrine-mongodb,Serialization,Zend Framework2,Doctrine Odm,Doctrine Mongodb,我试图通过读取文档的元数据来处理文档的序列化。我的灵感来自于使用条令ORM的实体,并对其进行了修改,以匹配条令ODM处理文档的方式。不幸的是,有些东西不能正常工作,因为一个文档从未被序列化多次,即使它被第二次引用,也会导致不完整的序列化 例如,它为属于某个位置1(请参阅)的user1(请参阅)输出此(json)。然后,它输出我们应该再次看到user1的位置和属于它的用户,但我们没有: { id: "505cac0d6803fa1e15000004", login: "user1",

我试图通过读取文档的元数据来处理文档的序列化。我的灵感来自于使用条令ORM的实体,并对其进行了修改,以匹配条令ODM处理文档的方式。不幸的是,有些东西不能正常工作,因为一个文档从未被序列化多次,即使它被第二次引用,也会导致不完整的序列化

例如,它为属于某个位置1(请参阅)的user1(请参阅)输出此(json)。然后,它输出我们应该再次看到user1的位置和属于它的用户,但我们没有:

{
  id: "505cac0d6803fa1e15000004",
  login: "user1",
  places: [
    {
      id: "505cac0d6803fa1e15000005",
      code: "place1",
      users: [
        {
          id: "505c862c6803fa6812000000",
          login: "user2"
        }
      ]
    }
  ]
}
我想这可能与阻止循环引用有关,但有办法解决吗

另外,我正在ZF2应用程序中使用它,是否有更好的方法使用ZF2序列化程序实现它


谢谢你的帮助。

我已经为DoctrineODM编写了一个序列化程序。您可以在-look-in
lib/Sds/DoctrineExtensions/Serializer
中找到它

如果您使用的是zf2,那么您可能还喜欢它,它配置DoctrineExtensions以在zf2中使用

要使用该模块,请与其他模块一样,使用composer安装它。然后将以下内容添加到zf2配置中:

'sds' => [
    'doctrineExtensions' => [
        'extensionConfigs' => [
            'Sds\DoctrineExtensions\Serializer' => null,
        ),
    ),
),
要获取序列化程序,请使用:

$serializer = $serivceLocator->get('Sds\DoctrineExtensions\Serializer');
要使用序列化程序,请执行以下操作:

$array = $serializer->toArray($document)
$json = $serializer->toJson($document)

$document = $serializer->fromArray($array)
$document = $serializer->fromJson($json)
如果要使用这些注释,还可以使用一些额外的注释来控制序列化:

@Sds\Setter - specify a non standard setter for a property
@Sds\Getter - specify a non standard getter fora  property
@Sds\Serializer(@Sds\Ignore) - ignore a property when serializing
这一切仍在进行中,因此任何意见/改进都将不胜感激。当您遇到这些LIB的问题时,只需将它们登录到github,它们就会得到及时的解决

最后,关于序列化嵌入文档和引用文档的注意事项—嵌入文档应与其父文档一起序列化,而引用文档不应序列化。这反映了数据保存在数据库中的方式。这也意味着循环引用不是问题

更新 我已经将更新推送到Sds/DoctrineExtensions/Serializer,以便它现在可以正确处理引用。更新了以下三(五)种方法:

toArray/toJson
fromArray/fromJson
applySerializeMetadataToArray
前两个是自解释的-最后一个是允许应用序列化规则,而不必将db结果添加到文档中

默认情况下,引用将序列化为如下数组:

[$ref: 'CollectionName/DocumentId']
/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('MyAlternativeSerializer'))
 */
protected $myDocumentProperty;
/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('Sds\DoctrineExtensions\Serializer\Reference\Eager'))
 */
protected $myDocumentProperty;
$ref
引用风格是Mongo内部使用的,因此它看起来很合适。给出的引用格式期望它可以用作RESTAPI的URL

通过定义一个替代的
ReferenceSerializer
可以覆盖默认行为,如下所示:

[$ref: 'CollectionName/DocumentId']
/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('MyAlternativeSerializer'))
 */
protected $myDocumentProperty;
/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('Sds\DoctrineExtensions\Serializer\Reference\Eager'))
 */
protected $myDocumentProperty;
库中已经包含了一个备用的
ReferenceSerializer
。它是热切的序列化程序-它将序列化引用,就像它们是嵌入的文档一样。它可以这样使用:

[$ref: 'CollectionName/DocumentId']
/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('MyAlternativeSerializer'))
 */
protected $myDocumentProperty;
/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('Sds\DoctrineExtensions\Serializer\Reference\Eager'))
 */
protected $myDocumentProperty;
或者提供另一种速记注释:

/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\Eager))
 */
protected $myDocumentProperty;
备用
referenceserializer
必须实现
Sds\DoctrineExtensions\Serializer\Reference\ReferenceSerializerInterface

此外,我还清理了ignore注释,因此可以将以下注释添加到属性中,以便对序列化进行更细粒度的控制:

@Sds\Serializer(@Sds\Ignore('ignore_when_serializing'))
@Sds\Serializer(@Sds\Ignore('ignore_when_unserializing'))
@Sds\Serializer(@Sds\Ignore('ignore_always'))
@Sds\Serializer(@Sds\Ignore('ignore_never'))
例如,将
@Sds\Serializer(@Sds\Ignore('Ignore_when_serialization'))
放在电子邮件属性上-这意味着电子邮件可以发送到服务器进行更新,但为了安全起见,永远不能序列化到客户端


最后,如果您没有注意到的话,sds注释支持继承和重写,因此它们可以很好地处理复杂的文档结构。

将Doctrine ODM文档转换为数组或JSON的另一种非常简单、独立于框架的方法-

此解决方案提供了一个特性,为ODM文档提供了
toArray()
toJSON()
函数。
使用
在文档中标记特征后,您可以执行以下操作-

<?php
// Assuming in a Symfony2 Controller
// If you're not, then make your DocmentManager as you want
$dm = $this->get('doctrine_mongodb')->getManager();
$report = $dm->getRepository('YourCoreBundle:Report')->find($id);

// Will return simple PHP array
$docArray = $report->toArray();

// Will return JSON string
$docJSON = $report->toJSON();

Hi@superdweebie,谢谢你的帮助。我尝试了您的序列化程序,但遇到了一个问题,即引用的文档仍然作为对象进行序列化和检索,因为似乎没有检查
$mapping['reference']
以不添加它们。另外,对于不返回字符串(如日期对象)的getter呢?另一方面,如果您不在序列化程序本身中检索文档,您将如何公开某种API来访问您的应用程序中引用文档的数据?嘿@jhuet,感谢您花时间试一试。我将为serializer ref文档添加一个测试用例(我想我没有)。至于API,我正在考虑使用如下内容:。我实际上还没有必要这样做,这就是为什么代码不是100%。但是这是一个很好的改变,可以使lib更加坚固一些。没问题:)dojox.json.ref看起来是一个有趣的方法,你知道它被广泛采用了吗?到目前为止,在我访问的任何API上,引用的文档都只是作为普通文档输出。顺便说一句,它似乎并不能解决如何检索这些引用的文档。在服务器端,如果文档是第一次发送,您如何知道它是作为参考还是作为普通文档?我怀疑客户是否应该在每次请求时发送他所知道的所有文档?@jhuet我刚刚推动了一些更改,如上所述。他们可能会有所帮助。为了回答我的问题,@superdweebie制作了一个伟大的教义库,他在下面描述了它,其中包含一个序列化程序。如果您使用即时抓取,那么您将返回所有文档。现在,虽然您可能会遇到循环引用问题,但我们正在努力实现一个最大嵌套深度选项。@laalto,我已经添加了使用此解决方案的代码示例。谢谢