Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] SerializedPath option not working on non-scalar types, which are serialized into scalar (eg. DateTime) #49494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
HonzaMatosik opened this issue Feb 22, 2023 · 3 comments

Comments

@HonzaMatosik
Copy link

Symfony version(s) affected

6.2.0

Description

If SerializedPath is set on non-scalar types, eg. DateTimeImmutable:

final class ObjectDummyWithContextAttributeAndSerializedPath
{
    public function __construct(
        #[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])]
        #[SerializedPath('[property][with_path]')]
        public \DateTimeImmutable $propertyWithPath,
    ) {
    }
}

Normalization fails with error:

Symfony\Component\Serializer\Exception\LogicException: The element you are trying to set is already populated: "[property][with_path]"

But DateTimeImmutable is serialized into string, so expected result is:

['property' => ['with_path' => '02-22-2023']

How to reproduce

I created unit test for this case, which should be sufficient for reproducing: HonzaMatosik@51f1969

Possible Solution

No response

Additional Context

No response

@emomaliev
Copy link

Faced the same problem.

Hey @boenner. What is the logical fallacy if properties are set to values?

if ($propertyAccessor->isReadable($data, $serializedPath) && null !== $propertyAccessor->getValue($data, $serializedPath)) {
throw new LogicException(sprintf('The element you are trying to set is already populated: "%s".', (string) $serializedPath));
}

Because of this construction, we get an exception when serializing an object, for example, to json.

A similar problem is described here #49225

@boenner
Copy link
Contributor

boenner commented Feb 23, 2023

The normalizer is trying to set the value multiple times because non-scalar values are pushed twice into updateData() in the AbstractObjectNormalizer. Skipping the first updateData() changes the order of elements in the serialized string, so I reverted that because of a possible BC break (see: #43534 (comment)). It seems like I didn't test the SerializedPath with non-scalar values afterwards. Sorry!

I took your tests, @HonzaMatosik, into a branch and changed the behavior: https://github.com/boenner/symfony/tree/serializer-serializedpath-with-non-scalar-types. Can you check if that fix works for you?

@emomaliev
Copy link

Hi this works great

nicolas-grekas added a commit that referenced this issue Mar 31, 2023
…enner)

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
symfony-splitter pushed a commit to symfony/serializer that referenced this issue Mar 31, 2023
…enner)

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 symfony/symfony#49494 and symfony/symfony#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 (symfony/symfony#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
-------

d82ec41d18 [Serializer] Fix serializedpath for non scalar types
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants