1515use Symfony \Component \PropertyAccess \Exception \NoSuchPropertyException ;
1616use Symfony \Component \PropertyInfo \PropertyTypeExtractorInterface ;
1717use Symfony \Component \PropertyInfo \Type ;
18+ use Symfony \Component \Serializer \Context \ObjectChildContextTrait ;
1819use Symfony \Component \Serializer \Encoder \CsvEncoder ;
1920use Symfony \Component \Serializer \Encoder \JsonEncoder ;
2021use Symfony \Component \Serializer \Encoder \XmlEncoder ;
2122use Symfony \Component \Serializer \Exception \ExtraAttributesException ;
2223use Symfony \Component \Serializer \Exception \LogicException ;
24+ use Symfony \Component \Serializer \Exception \MissingConstructorArgumentsException ;
2325use Symfony \Component \Serializer \Exception \NotNormalizableValueException ;
24- use Symfony \Component \Serializer \Exception \RuntimeException ;
26+ use Symfony \Component \Serializer \Instantiator \Instantiator ;
27+ use Symfony \Component \Serializer \Instantiator \InstantiatorInterface ;
2528use Symfony \Component \Serializer \Mapping \AttributeMetadataInterface ;
2629use Symfony \Component \Serializer \Mapping \ClassDiscriminatorFromClassMetadata ;
2730use Symfony \Component \Serializer \Mapping \ClassDiscriminatorResolverInterface ;
3336 *
3437 * @author Kévin Dunglas <[email protected] > 3538 */
36- abstract class AbstractObjectNormalizer extends AbstractNormalizer
39+ abstract class AbstractObjectNormalizer extends AbstractNormalizer implements DenormalizerAwareInterface
3740{
41+ use DenormalizerAwareTrait;
42+ use ObjectChildContextTrait;
43+
3844 /**
3945 * Set to true to respect the max depth metadata on fields.
4046 */
@@ -93,6 +99,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
9399 public const PRESERVE_EMPTY_OBJECTS = 'preserve_empty_objects ' ;
94100
95101 private $ propertyTypeExtractor ;
102+ private $ instantiator ;
96103 private $ typesCache = [];
97104 private $ attributesCache = [];
98105
@@ -103,7 +110,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
103110 */
104111 protected $ classDiscriminatorResolver ;
105112
106- public function __construct (ClassMetadataFactoryInterface $ classMetadataFactory = null , NameConverterInterface $ nameConverter = null , PropertyTypeExtractorInterface $ propertyTypeExtractor = null , ClassDiscriminatorResolverInterface $ classDiscriminatorResolver = null , callable $ objectClassResolver = null , array $ defaultContext = [])
113+ public function __construct (ClassMetadataFactoryInterface $ classMetadataFactory = null , NameConverterInterface $ nameConverter = null , PropertyTypeExtractorInterface $ propertyTypeExtractor = null , ClassDiscriminatorResolverInterface $ classDiscriminatorResolver = null , callable $ objectClassResolver = null , array $ defaultContext = [], InstantiatorInterface $ instantiator = null )
107114 {
108115 parent ::__construct ($ classMetadataFactory , $ nameConverter , $ defaultContext );
109116
@@ -120,6 +127,24 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory
120127 }
121128 $ this ->classDiscriminatorResolver = $ classDiscriminatorResolver ;
122129 $ this ->objectClassResolver = $ objectClassResolver ;
130+
131+ if (null === $ instantiator ) {
132+ $ instantiator = new Instantiator ($ classMetadataFactory , $ this ->classDiscriminatorResolver , $ propertyTypeExtractor , null , $ nameConverter , null );
133+
134+ if ($ this ->denormalizer instanceof DenormalizerInterface) {
135+ $ instantiator ->setDenormalizer ($ this ->denormalizer );
136+ }
137+ }
138+ $ this ->instantiator = $ instantiator ;
139+ }
140+
141+ public function setDenormalizer (DenormalizerInterface $ denormalizer )
142+ {
143+ $ this ->denormalizer = $ denormalizer ;
144+
145+ // Because we need a denormalizer in the Instantiator and we create it in the construct method, it won't get it.
146+ // So we are obliged to overwrite this method in order to give the denormalizer to the Instantiator.
147+ $ this ->instantiator ->setDenormalizer ($ denormalizer );
123148 }
124149
125150 /**
@@ -210,29 +235,6 @@ public function normalize($object, string $format = null, array $context = [])
210235 return $ data ;
211236 }
212237
213- /**
214- * {@inheritdoc}
215- */
216- protected function instantiateObject (array &$ data , string $ class , array &$ context , \ReflectionClass $ reflectionClass , $ allowedAttributes , string $ format = null )
217- {
218- if ($ this ->classDiscriminatorResolver && $ mapping = $ this ->classDiscriminatorResolver ->getMappingForClass ($ class )) {
219- if (!isset ($ data [$ mapping ->getTypeProperty ()])) {
220- throw new RuntimeException (sprintf ('Type property "%s" not found for the abstract object "%s". ' , $ mapping ->getTypeProperty (), $ class ));
221- }
222-
223- $ type = $ data [$ mapping ->getTypeProperty ()];
224- if (null === ($ mappedClass = $ mapping ->getClassForType ($ type ))) {
225- throw new RuntimeException (sprintf ('The type "%s" has no mapped class for the abstract object "%s". ' , $ type , $ class ));
226- }
227-
228- if ($ mappedClass !== $ class ) {
229- return $ this ->instantiateObject ($ data , $ mappedClass , $ context , new \ReflectionClass ($ mappedClass ), $ allowedAttributes , $ format );
230- }
231- }
232-
233- return parent ::instantiateObject ($ data , $ class , $ context , $ reflectionClass , $ allowedAttributes , $ format );
234- }
235-
236238 /**
237239 * Gets and caches attributes for the given object, format and context.
238240 *
@@ -307,8 +309,14 @@ public function denormalize($data, string $type, string $format = null, array $c
307309 $ normalizedData = $ this ->prepareForDenormalization ($ data );
308310 $ extraAttributes = [];
309311
310- $ reflectionClass = new \ReflectionClass ($ type );
311- $ object = $ this ->instantiateObject ($ normalizedData , $ type , $ context , $ reflectionClass , $ allowedAttributes , $ format );
312+ $ instantiatorResult = $ this ->instantiator ->instantiate ($ type , $ normalizedData , $ context , $ format );
313+ if ($ instantiatorResult ->hasFailed ()) {
314+ throw new MissingConstructorArgumentsException ($ instantiatorResult ->getError ());
315+ }
316+ $ object = $ instantiatorResult ->getObject ();
317+ $ normalizedData = $ instantiatorResult ->getUnusedData ();
318+ $ context = $ instantiatorResult ->getUnusedContext ();
319+
312320 $ resolvedClass = $ this ->objectClassResolver ? ($ this ->objectClassResolver )($ object ) : \get_class ($ object );
313321
314322 foreach ($ normalizedData as $ attribute => $ value ) {
@@ -595,23 +603,6 @@ private function isMaxDepthReached(array $attributesMetadata, string $class, str
595603 return false ;
596604 }
597605
598- /**
599- * Overwritten to update the cache key for the child.
600- *
601- * We must not mix up the attribute cache between parent and children.
602- *
603- * {@inheritdoc}
604- *
605- * @internal
606- */
607- protected function createChildContext (array $ parentContext , string $ attribute , ?string $ format ): array
608- {
609- $ context = parent ::createChildContext ($ parentContext , $ attribute , $ format );
610- $ context ['cache_key ' ] = $ this ->getCacheKey ($ format , $ context );
611-
612- return $ context ;
613- }
614-
615606 /**
616607 * Builds the cache key for the attributes cache.
617608 *
0 commit comments