Deserialize DateTime in Symfony Deserialize DateTime in Symfony symfony symfony

Deserialize DateTime in Symfony


As DateTime is a nested object, you should use PropertyInfo Component as described here —https://symfony.com/doc/current/components/serializer.html#recursive-denormalization-and-type-safety

The extraction of property information is performed by extractor classes.

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

There are 4 types of extractors:

  • ReflectionExtractor
  • PhpDocExtractor
  • SerializerExtractor
  • DoctrineExtractor

For example, using ReflectionExtractor you need to specify type hint for either params or return type. It also looks for constructor params (requires to be enabled explicitly)

class Item {   protected $date;   public function setDate(\DateTime $date) {...}   public function getDate() : \DateTime {...}}

Property Info is registered automatically when option set:

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

After that you need to override serializer service to use it, or define a custom one. And the last part — add DateTimeNormalizer, so DateTime can be processed by serializer.

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' ]

That's it.


This question break my brain recently, and I've two entities with dateTime property, the solution is custom denormalizer like this:

<?phpnamespace 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);}

And declare service like this :

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

It's ok for me, that work !


The only official way is seems to declare a 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