Trying to use the serializer component in Symfony 3.3. I struggle with entities having 'DateTime' members.
My config.yml serializer init:
serializer:
enable_annotations: true
Added this in service.yml:
datetime_method_normalizer:
class: Symfony\Component\Serializer\Normalizer\DateTimeNormalizer
public: false
tags: [serializer.normalizer]
The deserialized code looks like this:
$yml = [...] // It was created by serializer->serialize()
$serializer = $this->get('serializer');
$myObject = $serializer->deserialize($yml, MyObject::class, "yaml");
The error is get is: Expected argument of type "DateTime", "string" given
in in vendor/symfony/symfony/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php (line 204)
I think the DateTimeNormalizer::denormalize never gets called. Any idea how to bring it back to life?
Info: DateTimeNormalizer::__constructor() is called.
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:
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:
<?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);
}
And declare service like this :
# DateTime Hook Normalizer
Mybundle.normalizer.dateTimeHook:
class: 'MybundleBundle\Serializer\Normalizer\DateTimeHookNormalizer'
public: false
tags: [serializer.normalizer]
It's ok for me, that work !
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With