maker-bundle: [Serializer] Custom Normalizer broken after upgrading to 6.1
Symfony version(s) affected
6.1
Description
My custom Normalizer is broken since i’ve upgraded to Symfony 6.1. This problem seems to be related to the new Serializer Profiler.
Maybe I forgot something?
Thanks, Alex
How to reproduce
Just use this example :
https://symfony.com/doc/6.1/serializer/custom_normalizer.html
Possible Solution
No response
Additional Context
FileNormalizer::__construct(): Argument symfony/symfony#2 ($normalizer) must be of type Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer, Symfony\\Component\\Serializer\\Debug\\TraceableNormalizer given
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 18
- Comments: 60 (25 by maintainers)
Commits related to this issue
- bug #46511 [Serializer] Added missing __call to TraceableNormalizer and TraceableSerializer (danielburger1337) This PR was squashed before being merged into the 6.1 branch. Discussion ---------- [S... — committed to symfony/symfony by derrabus 2 years ago
- Revert api platform 2.7.0-alpha : serializer broken at 6.1 right now with bc break see https://github.com/symfony/symfony/issues/46471 — committed to components-web-app/api-components-bundle by silverbackdan 2 years ago
- bug #46625 [FrameworkBundle] Disable Serializer data collect by default (chalasr) This PR was merged into the 6.1 branch. Discussion ---------- [FrameworkBundle] Disable Serializer data collect by ... — committed to symfony/symfony by nicolas-grekas 2 years ago
- fix: symfony 6.1 bc break for custom normalizer https://github.com/symfony/symfony/issues/46471 — committed to numberninecms/cms by williarin 2 years ago
- fix: symfony 6.1 bc break for custom normalizer https://github.com/symfony/symfony/issues/46471 — committed to numberninecms/cms by williarin 2 years ago
- fix: symfony 6.1 bc break for custom normalizer https://github.com/symfony/symfony/issues/46471 — committed to numberninecms/cms by williarin 2 years ago
- bug #47764 [Serializer] fixed traceable decoration priorities (mtarld) This PR was merged into the 6.1 branch. Discussion ---------- [Serializer] fixed traceable decoration priorities | Q ... — committed to symfony/symfony by fabpot 2 years ago
- bug #47764 [Serializer] fixed traceable decoration priorities (mtarld) This PR was merged into the 6.1 branch. Discussion ---------- [Serializer] fixed traceable decoration priorities | Q ... — committed to symfony/serializer by fabpot 2 years ago
- minor #18779 [Serializer] Use `NormalizerInterface` instead of `ObjectNormalizer` (mtarld) This PR was merged into the 6.3 branch. Discussion ---------- [Serializer] Use `NormalizerInterface` inste... — committed to symfony/symfony-docs by OskarStark 9 months ago
It hasn’t been fixed in the sense that there is no fix to be made in the code imho. The docs need to be updated to explain about this issue and how to properly wire up your custom normalizer.
Here is an example on how I do it:
Obviously you can replace the
serializer.normalizer.objectservice with any normalizer you like.Now this example is using Symfony 6.3 with PHP 8.2, but the same principles apply for older versions although you’d need to make some modifications like replace the Autowire attribute etc.
A possible workaround, that would work with the new TraceableNormalizer, is to use the new
Autowireattribute:But I agree with @derrabus that it is somewhat crazy, that symfony injects an object with an incompatiable type.
I’m not to familiar with how symfonys dependency injection works internally, but is the “symfony/proxy-manager-bridge” package not able to solve this problem? A “lazy” service will be replaced by a proxy service, so wouldn’t it be possible to create a proxy object that has the TraceableNormalizer functionality? Currently the service definition gets replaced by a decorator.
The library the bridge is based on, Ocarmius/ProxyManager, has the Access Interceptor Value Holder Proxy concept. This should be able to solve the problem. Bascially I’m suggesting that the service is not replaced by “TraceableNormalizer” (this applies for all traceable decorators, like TraceableAuthenticator), but by a proxy object.
Not sure about that. In the documented case, the developer does not just want any normalizer implementation. They explicitly want the behavior of
ObjectNormalizerbecause it solves 95% of their problem already. The alternatives are either to extendObjectNormalizeror to work withNormalizerAwareInterfaceand work with priorities and flags that are pushed to the context.The former is a technique which we should discourage imho. And the latter feels a bit too complicated for what should be achieved here.
I think that the example from the docs is correct and should continue to work after an upgrade. We should treat this as a bug.
The serializer topic aside: If I autowire a constructor parameter with a
Footype, I find it astonishing that I receive a service that is not aFoo.this “possible workaround” does not work for me 😕 (Circular reference detected)
Well, I’m not sure the normalizer services were actually meant to be autowirable based on their class name. Supporting that means that we don’t support decorating them (and so that we cannot apply the traceable decorators on them).
To me, the autowirable names in core should only about interfaces for that reason.
I think this feature should be reverted. Nested normalizers are an overused method. Or the old and new should continue to work at the same time. I will continue with 6.0 for a while.
I am having the same issue. Let me know if you need any help reproducing this one.
It happens when a Custom Normalizer is autowired with the ObjectNormalizer service.
I found a solution.
Actually, with a fresh Symfony instance, I managed to make it work.
You can leverage the
NormalizerAwareInterfacelike the following:Or using a constructor injection like the following:
In either way, you must never use a concrete implementation of any normalizer/encoder in the code, you should inject the interface and configure your container to inject the proper instance. Therefore, IMHO the maker bundle should be updated accordingly.
And if you don’t want to specify which normalizer instance to use because you don’t care, be aware that the constructor injection won’t work because of a circular reference exception (with and without the debug), therefore my advice is to always use the
NormalizerAwareInterface.Nope, it won’t: https://3v4l.org/sKOnn
That’s my personal preference.
Ok the only way I found to inject denormalizer into my own denormalizer is to disable autowiring and inject @serializer.normalizer.object directly.
See symfony/symfony#46625
Sorry if this is a dumb question, but can we not fix this in the DIC, making sure that if a decorated service is selected to be autowired, but the decorator does not satisfy the type hint, it would skip decoration and inject the decorated service?
Possibly this could be done optionally so that decorators could be marked optional (as in this case, it’s a “nice to have” feature, a user defined decorator I would assume should error if it cannot be autowired as such).
Actually, my perspective is that we must rely on abstraction here, and it looks weird to me to inject a concrete normalizer implementation. If anyone wants to inject the specific
ObjectNormalizer, he might inject aNormalizerInterfaceand let the dependency injection bind the proper implementation.That being said, it’s a BC break, therefore so far I can see three ways to go:
Serializer, this will imply that we won’t be able to retrieve anymore any nested normalizer, only the first selected normalizer will be traced.My point is that this should be done via an explicit service definition so the code only knows about NormalizerInterface, and the config tells which implementation should actually be injected. Targeting ObjectNormalizer directly as a constructor arg should be avoided from a design POV. Same for the “decoration” use case, proper decoration is made against an abstraction, not a concrete implementation.
That is a good point. This could be easily fixed by adding php’s magic
__callmethod to the TraceableNormalizer implementation. As far as I can see, exactly this was already implemented for TreaceableAuthenticator, TraceableEventDispatcher, LoggingTranslator etc…I can submit a PR incase this function wasn’t omitted on purpose?
Do you import that Autowire class, I had it not imported and this was leading to “Circular reference detected” error. After import seems tat it works in dev env
Similar (?) issue. On 6.0, the following service declaration works:
services.yamlOn 6.1 I suddenly get the error:
If I change my service declaration:
The error message changes: