5

尝试在 Symfony 3.3 中使用序列化程序组件。我与拥有“日期时间”成员的实体斗争。

我的 config.yml 序列化程序初始化:

serializer:
    enable_annotations: true

在 service.yml 中添加了这个:

datetime_method_normalizer:
    class: Symfony\Component\Serializer\Normalizer\DateTimeNormalizer
    public: false
    tags: [serializer.normalizer]

反序列化的代码如下所示:

$yml = [...]   // It was created by serializer->serialize()
$serializer = $this->get('serializer');
$myObject = $serializer->deserialize($yml, MyObject::class, "yaml");

错误是:Expected argument of type "DateTime", "string" given在 vendor/symfony/symfony/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php 中(第 204 行)

我认为 DateTimeNormalizer::denormalize 永远不会被调用。知道如何让它恢复生机吗?

信息: DateTimeNormalizer::__constructor() 被调用。

4

3 回答 3

4

由于 DateTime 是一个嵌套对象,您应该使用此处描述的 PropertyInfo 组件 — https://symfony.com/doc/current/components/serializer.html#recursive-denormalization-and-type-safety

属性信息的提取由提取器类执行。

https://symfony.com/doc/current/components/property_info.html#extractors

有 4 种类型的提取器:

  • 反射提取器
  • PhpDocExtractor
  • 序列化器提取器
  • 教义提取器

例如,使用 ReflectionExtractor 您需要为参数或返回类型指定类型提示。它还查找构造函数参数(需要显式启用)

class Item {

   protected $date;

   public function setDate(\DateTime $date) {...}
   public function getDate() : \DateTime {...}
}

设置选项时自动注册属性信息:

# config/packages/framework.yaml 
framework:
  property_info: ~

之后,您需要覆盖序列化程序服务才能使用它,或者定义一个自定义服务。最后一部分——添加 DateTimeNormalizer,这样 DateTime 就可以被序列化器处理了。

app.normalizer.item_normalizer:
    class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
    arguments:
      - null
      - null
      - null
      - '@property_info.reflection_extractor'
    tags: [ 'serializer.normalizer' ]

app.serializer.item:
    class: Symfony\Component\Serializer\Serializer
    public: true
    arguments:
      - [
          '@serializer.normalizer.datetime',
          '@app.normalizer.item_normalizer',
        ]
      - [ '@serializer.encoder.json' ]

而已。

于 2018-10-30T04:16:31.787 回答
2

这个问题最近让我大吃一惊,我有两个具有 dateTime 属性的实体,解决方案是自定义非规范化器,如下所示:

<?php

namespace MyBundle\Serializer\Normalizer;

use MyBundle\Entity\MyEntity1;
use MyBundle\Entity\MyEntity2;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

/**
 * DateTime hook normalizer
 */
class DateTimeHookNormalizer implements DenormalizerInterface
{
    /**
     * {@inheritdoc}
     */
    public function denormalize($data, $class, $format = null, array $context = array())
    {
        if (isset($data['MyDateTime1']) && is_string($data['MyDateTime1']))
        {
            $data['MyDateTime1'] = new \DateTime($data['MyDateTime1']);
        }
        if (isset($data['MyDateTime2']) && is_string($data['MyDateTime2']))
        {
            $data['MyDateTime2'] = new \DateTime($data['MyDateTime2']);
        }
        And more ...

        $normalizer = new ObjectNormalizer();//default normalizer

        return $normalizer->denormalize($data, $class, $format, $context);
    }
}

/**
 * {@inheritdoc}
 */
public function supportsDenormalization($data, $type, $format = null)
{
    return is_array($data) && ($type === MyEntity1::class || $type === MyEntity2::class);
}

并像这样声明服务:

# DateTime Hook Normalizer
Mybundle.normalizer.dateTimeHook:
    class: 'MybundleBundle\Serializer\Normalizer\DateTimeHookNormalizer'
    public: false
    tags: [serializer.normalizer]

对我来说没关系,那工作!

于 2017-09-20T00:00:02.847 回答
0

唯一的官方方式似乎是声明一个callback

$callback = function ($dateTime) {
return $dateTime instanceof \DateTime
    ? $dateTime->format(\DateTime::ISO8601)
    : '';
};

$normalizer->setCallbacks(array('createdAt' => $callback));
$serializer = new Serializer(array($normalizer), array($encoder));

https://symfony.com/doc/current/components/serializer.html#using-callbacks-to-serialize-properties-with-object-instances

于 2017-06-26T13:42:45.397 回答