在Symfony中反序列化DateTime

时间:2017-06-26 11:51:36

标签: symfony deserialization symfony-3.3

尝试在Symfony 3.3中使用序列化程序组件。我对拥有'DateTime'成员的实体感到困惑。

我的config.yml序列化程序init:

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");

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

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

Info:调用DateTimeNormalizer :: __ constructor()。

3 个答案:

答案 0 :(得分: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]

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

答案 1 :(得分:2)

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

  

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

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

有4种类型的提取器:

  • ReflectionExtractor
  • PhpDocExtractor
  • SerializerExtractor
  • DoctrineExtractor

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

class Item {

   protected $date;

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

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

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

此后,您需要覆盖 serializer 服务以使用它,或定义一个自定义服务。最后一部分-添加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' ]

就是这样。

答案 2 :(得分: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