Skip to content

[Serializer] Add NormalizedValueInterface that can be returned by NormalizerInterface::normalize to enable receiving a value object for normalized values. #43498

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

Open
wants to merge 3 commits into
base: 6.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Component/Serializer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CHANGELOG

* Remove `ArrayDenormalizer::setSerializer()`, call `setDenormalizer()` instead
* Remove the ability to create instances of the annotation classes by passing an array of parameters, use named arguments instead
* Add `NormalizedValueInterface` to enable normalizers to return a value object

5.4
---
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Normalizer;

interface NormalizedValueInterface
{
public function getValue(): array|string|int|float|bool|\ArrayObject|null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ interface NormalizerInterface
* @param string $format Format the normalization result will be encoded as
* @param array $context Context options for the normalizer
*
* @return array|string|int|float|bool|\ArrayObject|null \ArrayObject is used to make sure an empty object is encoded as an object not an array
* @return array|string|int|float|bool|\ArrayObject|NormalizedValueInterface|null \ArrayObject is used to make sure an empty object is encoded as an object not an array
*
* @throws InvalidArgumentException Occurs when the object given is not a supported type for the normalizer
* @throws CircularReferenceException Occurs when the normalizer detects a circular reference when no circular
* reference handler can fix it
* @throws LogicException Occurs when the normalizer is not called in an expected context
* @throws ExceptionInterface Occurs for all the other cases of errors
*/
public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null;
public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|NormalizedValueInterface|null;

/**
* Checks whether the given class is supported for normalization by this normalizer.
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/Serializer/Serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizedValueInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

Expand Down Expand Up @@ -161,6 +162,10 @@ public function normalize(mixed $data, string $format = null, array $context = [
return $normalizer->normalize($data, $format, $context);
}

if ($data instanceof NormalizedValueInterface) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion the serializer class should return the wrapper object too.

We have a use case in API Platform: we'll be able to return an object containing the serialized data and some additional metadata. We need to be able to access the metadata from the outside.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome to hear there is an reason to have this in api platform. I will have a look later today too see how that might work.

I didn't initially since i thought that would be a bigger bc break and if you want to access the data you could just have your own serializer.

I do see your point though

$data = $data->getValue();
}

if (null === $data || is_scalar($data)) {
return $data;
}
Expand Down
15 changes: 15 additions & 0 deletions src/Symfony/Component/Serializer/Tests/SerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizedValueInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
Expand Down Expand Up @@ -211,6 +212,20 @@ public function testSerializeArrayOfScalars()
$this->assertEquals(json_encode($data), $result);
}

public function testSerializeNormalizedValue()
{
$normalizedValue = 'normalizedValue';
$normalizedValueObject = $this->createMock(NormalizedValueInterface::class);
$normalizedValueObject->method('getValue')
->willReturn($normalizedValue);

$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$data = ['foo', [5, $normalizedValueObject]];
$expectedData = ['foo', [5, $normalizedValue]];
$result = $serializer->serialize($data, 'json');
$this->assertEquals(json_encode($expectedData), $result);
}

public function testSerializeEmpty()
{
$serializer = new Serializer([new ObjectNormalizer()], ['json' => new JsonEncoder()]);
Expand Down