Skip to content

[Serializer] Custom normalizer example is recursive #19068

Closed as not planned
Closed as not planned
@jennevdmeer

Description

@jennevdmeer

The 7.0 example for the custom normalizer (https://symfony.com/doc/7.0/serializer/custom_normalizer.html) is a recursive example and could use more details.

class TopicNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
public function __construct(
private UrlGeneratorInterface $router,
) {
}
public function normalize($topic, string $format = null, array $context = []): array
{
$data = $this->normalizer->normalize($topic, $format, $context);

Using NormalizerAwareInterface and NormalizerAwareTrait will inject the full instance of the Serializer class (
https://github.com/symfony/symfony/blob/d741f387eedb2a25cd513cb6472e3456baa5fe4e/src/Symfony/Component/Serializer/Serializer.php#L92-L94).

The $this->normalizer->normalize(... call (line 40 above) would then just call the normalize on the same serializer and end up in the same normalizer recursively.

After a discussion in slack with @Pixelshaped having questions with the public function getSupportedTypes(?string $format): array; and Stof joining in we came to a few conclusions;

  • Stof mentioned: The basic example of a custom normalizer should use a different use case that this re-entrant normalization that require avoiding infinite loops, I though maybe a custom normalizer for a Money type (amount/currency) could work as an example but any recommendations are appreciated.
  • As from @Pixelshaped initial questions the Performance of Normalizers/Denormalizers section could use a better description/example as to when a normalizer can or can not be cached (I've seen this question a few times in slack, it seems not really clear from the docs).
    Most confusion is that people think it would cache the normalization result. While in fact it just caches if the normalizer class should be reused directly from the hashmap, without checking supportsNormalization if data is of said specific type(s).
  • One thing I found missing was does inheritance work when defining getSupportedTypes types. Afaik this is false(?) as it creates a hashmap for lookup, but might be worth mentioning.

To help with the getSupportedTypes the current example could be used with a proper workaround like described at API platform or SymfonyCasts by using some kind of marker in the context. Or if anyone knows a different (easier) use case to clearly show the difference between CAN and CAN NOT be cached please!

Which effectively makes the normalize not cacheable as supportsNormalization needs to be handled on an object by object case.

And finally since 6.3 docs there is a info block saying "Injecting ObjectNormalizer is deprecated, however the example above it does not show how to properly do it. I don't know if its common to have it like this but I would think having a proper example (same as mentioned above?) there would be useful to get rid of the deprecation.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions