phpsymfonysymfony-serializer

How to handle attributes for properties in custom serializer?


I have app based on symfony5.4. Inside this app I have a class for client like this:

class Client
{
    #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
    public \DateTimeImmutable $passportIssuedDate;

    #[Context([DateTimeNormalizer::FORMAT_KEY => \DateTimeImmutable::ATOM])]
    public \DateTimeImmutable $agreementDateTime;
}

There is custom serializer:

$encoders = [new JsonEncoder()];
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
$normalizers = [
    new DateTimeNormalizer(),
    new ObjectNormalizer(propertyTypeExtractor: $extractor),
];
$serializer = new Serializer($normalizers, $encoders);

If I call standard autowired serializer

$this->serializer->serialize($client, 'json');

I get this result

{"passportIssuedDate":"2024-04-05","agreementDateTime":"2024-04-05T11:59:32+00:00"}

But in case of custom one I see this:

{"passportIssuedDate":"2024-04-05T11:59:32+00:00","agreementDateTime":"2024-04-05T11:59:32+00:00"}

How I need to change my custom serializer to handle Context attributes properly?


Solution

  • When you create your ObjectNormalizer, you need to pass a ClassMetadataFactory created from an AttributeLoader:

    require('vendor/autoload.php');
    
    use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
    use Symfony\Component\Serializer\Serializer;
    use Symfony\Component\Serializer\Annotation\Context;
    use Symfony\Component\Serializer\Encoder\JsonEncoder;
    use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
    use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
    use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
    use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    
    class Client
    {
        #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
        public \DateTimeImmutable $passportIssuedDate;
    
        #[Context([DateTimeNormalizer::FORMAT_KEY => \DateTimeImmutable::ATOM])]
        public \DateTimeImmutable $agreementDateTime;
    }
    
    $encoders = [new JsonEncoder()];
    $extractor = new ReflectionExtractor();
    $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
    $normalizers = [
        new DateTimeNormalizer(),
        new ObjectNormalizer($classMetadataFactory, null, null, $extractor),
    ];
    $serializer = new Serializer($normalizers, $encoders);
    
    $client = new Client();
    $client->passportIssuedDate = new DateTimeImmutable('2024-04-05');
    $client->agreementDateTime = new DateTimeImmutable('2024-04-05');
    echo $serializer->serialize($client, 'json');
    

    Output:

    {"passportIssuedDate":"2024-04-05","agreementDateTime":"2024-04-05T00:00:00+00:00"}