Skip to content

Commit 4f2aaf9

Browse files
bug #49525 [Serializer] Fix serialized path for non-scalar values (boenner)
This PR was merged into the 6.2 branch. Discussion ---------- [Serializer] Fix serialized path for non-scalar values | Q | A | ------------- | --- | Branch? | 6.2 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #49494 | License | MIT | Doc PR | no This relates to #49494 and #49225. When non-scalar values are normalized, they are normalized twice in the `normalize()` function: ```php if (null !== $attributeValue && !\is_scalar($attributeValue)) { $stack[$attribute] = $attributeValue; } $data = $this->updateData($data, $attribute, $attributeValue, $class, $format, $attributeContext, $attributesMetadata, $classMetadata); ``` and a bit later: ```php foreach ($stack as $attribute => $attributeValue) { ... $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $childContext), $class, $format, $attributeContext, $attributesMetadata, $classMetadata); } ``` For non-scalar values with a `SerializedPath` annotation this leads to an exception because the serializer is trying to re-populate the path. Running `updateData()` only once fixes this, but breaks a couple of tests across the components as it changes the order of elements in the serialized string (non-scalar values will be pushed to the end). Other than the string comparisons, nothing seems to break. This was also an issue while reviewing the PR for the `SerializedPath` annotation (#43534 (comment)) and got reverted because of the potential BC break. I'm not sure what benefit normalizing twice brings, so I added the test from `@HonzaMatosik`, changed the behavior in the normalizer and fixed the broken tests. If that's not the preferred solution here, I'd be ok with just eleminating the "The element you are trying to set is already populated" exception in the `SerializedPath` and allow overwriting values. Commits ------- d82ec41 [Serializer] Fix serializedpath for non scalar types
2 parents 97b928e + d82ec41 commit 4f2aaf9

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,15 @@ public function normalize(mixed $object, string $format = null, array $context =
198198

199199
$attributeValue = $this->applyCallbacks($attributeValue, $object, $attribute, $format, $attributeContext);
200200

201-
if (null !== $attributeValue && !\is_scalar($attributeValue)) {
202-
$stack[$attribute] = $attributeValue;
203-
}
204-
205-
$data = $this->updateData($data, $attribute, $attributeValue, $class, $format, $attributeContext, $attributesMetadata, $classMetadata);
201+
$stack[$attribute] = $attributeValue;
206202
}
207203

208204
foreach ($stack as $attribute => $attributeValue) {
205+
if (null === $attributeValue || \is_scalar($attributeValue)) {
206+
$data = $this->updateData($data, $attribute, $attributeValue, $class, $format, $context, $attributesMetadata, $classMetadata);
207+
continue;
208+
}
209+
209210
if (!$this->serializer instanceof NormalizerInterface) {
210211
throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer.', $attribute));
211212
}

src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,21 @@ public function testDenormalizeUsesContextAttributeForPropertiesInConstructorWit
542542

543543
$this->assertSame($obj->propertyWithSerializedName->format('Y-m-d'), $obj->propertyWithoutSerializedName->format('Y-m-d'));
544544
}
545+
546+
public function testNormalizeUsesContextAttributeForPropertiesInConstructorWithSerializedPath()
547+
{
548+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
549+
550+
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
551+
$normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory), null, $extractor);
552+
$serializer = new Serializer([new DateTimeNormalizer([DateTimeNormalizer::FORMAT_KEY => 'd-m-Y']), $normalizer]);
553+
554+
$obj = new ObjectDummyWithContextAttributeAndSerializedPath(new \DateTimeImmutable('22-02-2023'));
555+
556+
$data = $serializer->normalize($obj);
557+
558+
$this->assertSame(['property' => ['with_path' => '22-02-2023']], $data);
559+
}
545560
}
546561

547562
class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
@@ -643,6 +658,16 @@ class DuplicateKeyNestedDummy
643658
public $notquux;
644659
}
645660

661+
class ObjectDummyWithContextAttributeAndSerializedPath
662+
{
663+
public function __construct(
664+
#[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])]
665+
#[SerializedPath('[property][with_path]')]
666+
public \DateTimeImmutable $propertyWithPath,
667+
) {
668+
}
669+
}
670+
646671
class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer
647672
{
648673
public function __construct()

0 commit comments

Comments
 (0)