Uploaded image for project: 'eZ Publish / Platform'
  1. eZ Publish / Platform
  2. EZP-32428

When using Symfony Serializer service to deserialize data, the ObjectNormalizer is never called

    XMLWordPrintable

Details

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: High High
    • None
    • 2.5.24, 3.3.10
    • None
    • None

    Description

      I ran into a weird issue when using the Symfony Serializer service: when I use it to deserialize data (e.g. a JSON string) into an object (a simple DTO with public properties), it does not use default behaviour of the serializer.

      For example, when using a name converter (`serializer.name_converter.camel_case_to_snake_case`), the snake case properties are not mapped in CamelCase properties. Also, the `@SerializedName` annotation on the DTO is neither taken into account.

      Steps to reproduce :

      • Create a new Ibexa DXP OSS application (I tested on 3.3 but it may be the same on older versions)
      • In `config/packages/framework.yaml`, add the following lines :
      framework:
          # ...
          serializer:
              enabled: true
              name_converter: 'serializer.name_converter.camel_case_to_snake_case'
      • Create a DTO with 2 public properties, e.g.:
      <?php
      
      declare(strict_types=1);
      
      namespace App\Model;
      
      final class Test
      {
          public $myProperty;
      
          public $myOtherProperty;
      }
      
      • Create a basic controller, inject the serializer, and call it to deserialize a JSON string into your DTO `Test`, e.g.:
      <?php
      
      declare(strict_types=1);
      
      namespace App\Controller;
      
      use App\Model\Test;
      use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
      use Symfony\Component\HttpFoundation\Response;
      use Symfony\Component\Routing\Annotation\Route;
      use Symfony\Component\Serializer\Encoder\JsonEncoder;
      use Symfony\Component\Serializer\SerializerInterface;
      use function dd;
      
      final class TestController extends AbstractController
      {
          private SerializerInterface $serializer;
      
          public function __construct(SerializerInterface $serializer)
          {
              $this->serializer = $serializer;
          }
      
          /**
           * @Route("/test")
           */
          public function __invoke(): Response
          {
              $test = $this->serializer->deserialize('{"my_property": "foo", "my_other_property": 5}', Test::class, JsonEncoder::FORMAT);
      
              dd($test);
          }
      }
      
      •  Open `/test` page in your browser

      Expected behaviour:

      The object `$test` is correctly deserialized and all its properties have been mapped, thanks to `serializer.name_converter.camel_case_to_snake_case` name converter.

      Actual behaviour:

      The object is created, but all its properties are null.

      Additional information:

      I investigated a bit further, and it seems that the Symfony built in normalizers are never called. Instead, the normalizer which is called is `eZ\Publish\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer`, which is an internal Ibexa normalizer.

      In fact, it is called during denormalization because it extends `Symfony\Component\Serializer\Normalizer\PropertyNormalizer`, and doesn't override the `supportsDenormalization` method. As it has a higher priority than the Symfony's built in normalizers, it is called but should not be called in this context.

      Another weird thing that might explain, is that this Ibexa normalizer is the only one declared as service and tagged as `serializer.normalizer`, for example `eZ\Publish\Core\MVC\Symfony\Component\Serializer\HostElementNormalizer`. Other normalizer are only referenced in `eZ\Publish\Core\MVC\Symfony\Component\Serializer\SerializerTrait`.

      Suggestions to fix the issue:

      I think there are 2 ways to fix the issue:

      • Don't declare `eZ\Publish\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer` as a service, and only reference it in `eZ\Publish\Core\MVC\Symfony\Component\Serializer\SerializerTrait`
      • Add a method `supportsDenormalization` more restrictive than parent one.

      Attachments

        Activity

          People

            Unassigned Unassigned
            bertinremi31@gmail.com bertinremi31@gmail.com
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: