doctrine-json-odm icon indicating copy to clipboard operation
doctrine-json-odm copied to clipboard

Can't get to work with DateTime denormalization

Open ghostika opened this issue 5 years ago • 10 comments

I've used previously the 0.1.3 version and wanted to update it to v1. I've done the setup according to the documentation, just that 3 normalizer, but somehow my dateTime is not deserialized. I've checked it with xdebug and all 3 normalizers are registered and they have the right order.

My json string:

[{"#type": "App\\Entity\\EventSchedule", "endDate": "2020-04-11T00:00:00+02:00", "startDate": "2020-01-07T00:00:00+01:00"}]

EventSchedule

class EventSchedule
{
    /**
     * Start date.
     *
     * @var DateTimeImmutable
     */
    private $startDate;

    /**
     * End date.
     *
     * @var DateTimeImmutable
     */
    private $endDate;
}

Error message: Failed to denormalize attribute "endDate" value for class "App\Entity\EventSchedule": Expected argument of type "DateTimeImmutable", "string" given at property path "endDate"

I would highly appreciate any help. I have ApiPlatform installed, but in this request, it's not used. I tried just calling the service in the controller and passing the string to the deserialize event.

ghostika avatar Jan 16 '20 17:01 ghostika

Did you check the FAQ? https://github.com/dunglas/doctrine-json-odm#faq

Toflar avatar Jan 16 '20 17:01 Toflar

Yes, that's what I meant with documentation. And the 3 normalizers, what are under the FAQ are registered.

ghostika avatar Jan 16 '20 17:01 ghostika

Okay. So if you're using xdebug you should see if the datetime normalizer is called, right?

Toflar avatar Jan 16 '20 17:01 Toflar

Yes, and that's the problem that it's not called but the DateTimeNormalizer is one of the 3 normalizers and I can't figure it out why.

ghostika avatar Jan 16 '20 18:01 ghostika

I've taken a look at it again and I will try to create tomorrow an example SF app for this. What I've found with XDebug that it finds the DateTimeDenormalizer and when it calls in the serializer the dernormalize methode, it calls the denormalize methode from Dunglas Serializer. As the data is 2020-04-11T00:00:00+02:00 it skips the first if and not iterable, so skips the 2nd if and just simply returns the data, without a parent::denormalize call.

ghostika avatar Jan 16 '20 20:01 ghostika

Ok, I know what the problem is and it's connected to my previous comment. I've found no update.MD file so I thought the old serialized data should be comatible with the new. I did a serialization and that is the result:

[{"#type": "App\\Entity\\Event\\EventSchedule", "endDate":{"#type":"DateTimeImmutable","#scalar":"2020-04-11T00:00:00+02:00"}, "startDate": {"#type":"DateTimeImmutable","#scalar":"2020-01-07T00:00:00+01:00"}}]

instead of the result in the DB with version 0.1.3

[{"#type": "App\\Entity\\EventSchedule", "endDate": "2020-04-11T00:00:00+02:00", "startDate": "2020-01-07T00:00:00+01:00"}]

I think this should be documented somewhere.

ghostika avatar Jan 17 '20 07:01 ghostika

Or am I doing something wrong here @Toflar ?

ghostika avatar Feb 01 '20 12:02 ghostika

You can always use the compiler pass to add all the normalizers & encoders:

$container->addCompilerPass(new SerializerPass('dunglas_doctrine_json_odm.serializer'));

TNAJanssen avatar Mar 26 '20 00:03 TNAJanssen

@dunglas i changed the Symfony SerializerPass to work with your code, this will prevent these types of issues:

<?php

namespace App\Shared\Infrastructure\Symfony\Serializer;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;

class SerializerPass implements CompilerPassInterface
{
    use PriorityTaggedServiceTrait;

    private string $serializerService;
    private string $normalizerTag;
    private string $encoderTag;

    public function __construct(string $serializerService = 'dunglas_doctrine_json_odm.serializer', string $normalizerTag = 'serializer.normalizer', string $encoderTag = 'serializer.encoder')
    {
        $this->serializerService = $serializerService;
        $this->normalizerTag = $normalizerTag;
        $this->encoderTag = $encoderTag;
    }

    public function process(ContainerBuilder $container): void
    {
        if (!$container->hasDefinition($this->serializerService)) {
            return;
        }

        if (!$normalizers = $this->findAndSortTaggedServices($this->normalizerTag, $container)) {
            throw new RuntimeException(sprintf('You must tag at least one service as "%s" to use the "%s" service.', $this->normalizerTag, $this->serializerService));
        }

        array_unshift($normalizers, new Reference('dunglas_doctrine_json_odm.normalizer.array'));
        $normalizers[] = new Reference('dunglas_doctrine_json_odm.normalizer.object');

        $serializerDefinition = $container->getDefinition($this->serializerService);
        $serializerDefinition->replaceArgument(0, $normalizers);

        if (!$encoders = $this->findAndSortTaggedServices($this->encoderTag, $container)) {
            throw new RuntimeException(sprintf('You must tag at least one service as "%s" to use the "%s" service.', $this->encoderTag, $this->serializerService));
        }

        $serializerDefinition->replaceArgument(1, $encoders);
    }
}

TNAJanssen avatar Mar 26 '20 19:03 TNAJanssen

For me it was only 20 entries in the database, I just changed it with a script, but I think it would be good to mentions this difference.

ghostika avatar Mar 26 '20 20:03 ghostika