feature #35235 [Serializer] Added scalar denormalization (a-menshchikov)

This PR was merged into the 5.1-dev branch.

Discussion
----------

[Serializer] Added scalar denormalization

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #33784
| License       | MIT

Was added an ability to deserialize scalar data (single or array).

Commits
-------

dad04d0adf Added scalar denormalization in Serializer + added scalar normalization tests
This commit is contained in:
Maxime Steinhausser 2020-01-13 09:54:25 +01:00
commit ddc016988e
3 changed files with 103 additions and 2 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
5.1.0
-----
* added support for scalar values denormalization
5.0.0
-----
@ -12,7 +17,7 @@ CHANGELOG
`AbstractNormalizer::$camelizedAttributes`, `AbstractNormalizer::setCircularReferenceLimit()`,
`AbstractNormalizer::setCircularReferenceHandler()`, `AbstractNormalizer::setCallbacks()` and
`AbstractNormalizer::setIgnoredAttributes()`, use the default context instead.
* removed `AbstractObjectNormalizer::$maxDepthHandler` and `AbstractObjectNormalizer::setMaxDepthHandler()`,
* removed `AbstractObjectNormalizer::$maxDepthHandler` and `AbstractObjectNormalizer::setMaxDepthHandler()`,
use the default context instead.
* removed `XmlEncoder::setRootNodeName()` & `XmlEncoder::getRootNodeName()`, use the default context instead.
* removed individual encoders/normalizers options as constructor arguments.

View File

@ -46,6 +46,13 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
*/
class Serializer implements SerializerInterface, ContextAwareNormalizerInterface, ContextAwareDenormalizerInterface, ContextAwareEncoderInterface, ContextAwareDecoderInterface
{
private const SCALAR_TYPES = [
'int' => true,
'bool' => true,
'float' => true,
'string' => true,
];
/**
* @var Encoder\ChainEncoder
*/
@ -177,6 +184,14 @@ class Serializer implements SerializerInterface, ContextAwareNormalizerInterface
*/
public function denormalize($data, string $type, string $format = null, array $context = [])
{
if (isset(self::SCALAR_TYPES[$type])) {
if (!('is_'.$type)($data)) {
throw new NotNormalizableValueException(sprintf('Data expected to be of type "%s" ("%s" given)', $type, \is_object($data) ? \get_class($data) : \gettype($data)));
}
return $data;
}
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
}
@ -201,7 +216,7 @@ class Serializer implements SerializerInterface, ContextAwareNormalizerInterface
*/
public function supportsDenormalization($data, string $type, string $format = null, array $context = [])
{
return null !== $this->getDenormalizer($data, $type, $format, $context);
return isset(self::SCALAR_TYPES[$type]) || null !== $this->getDenormalizer($data, $type, $format, $context);
}
/**

View File

@ -17,6 +17,7 @@ use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
@ -488,6 +489,86 @@ class SerializerTest extends TestCase
(new Serializer())->normalize(tmpfile());
}
public function testNormalizeScalar()
{
$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$this->assertSame('42', $serializer->serialize(42, 'json'));
$this->assertSame('true', $serializer->serialize(true, 'json'));
$this->assertSame('false', $serializer->serialize(false, 'json'));
$this->assertSame('3.14', $serializer->serialize(3.14, 'json'));
$this->assertSame('3.14', $serializer->serialize(31.4e-1, 'json'));
$this->assertSame('" spaces "', $serializer->serialize(' spaces ', 'json'));
$this->assertSame('"@Ca$e%"', $serializer->serialize('@Ca$e%', 'json'));
}
public function testNormalizeScalarArray()
{
$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$this->assertSame('[42]', $serializer->serialize([42], 'json'));
$this->assertSame('[true,false]', $serializer->serialize([true, false], 'json'));
$this->assertSame('[3.14,3.24]', $serializer->serialize([3.14, 32.4e-1], 'json'));
$this->assertSame('[" spaces ","@Ca$e%"]', $serializer->serialize([' spaces ', '@Ca$e%'], 'json'));
}
public function testDeserializeScalar()
{
$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$this->assertSame(42, $serializer->deserialize('42', 'int', 'json'));
$this->assertTrue($serializer->deserialize('true', 'bool', 'json'));
$this->assertSame(3.14, $serializer->deserialize('3.14', 'float', 'json'));
$this->assertSame(3.14, $serializer->deserialize('31.4e-1', 'float', 'json'));
$this->assertSame(' spaces ', $serializer->deserialize('" spaces "', 'string', 'json'));
$this->assertSame('@Ca$e%', $serializer->deserialize('"@Ca$e%"', 'string', 'json'));
}
public function testDeserializeLegacyScalarType()
{
$this->expectException(LogicException::class);
$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$serializer->deserialize('42', 'integer', 'json');
}
public function testDeserializeScalarTypeToCustomType()
{
$this->expectException(LogicException::class);
$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$serializer->deserialize('"something"', Foo::class, 'json');
}
public function testDeserializeNonscalarTypeToScalar()
{
$this->expectException(NotNormalizableValueException::class);
$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$serializer->deserialize('{"foo":true}', 'string', 'json');
}
public function testDeserializeInconsistentScalarType()
{
$this->expectException(NotNormalizableValueException::class);
$serializer = new Serializer([], ['json' => new JsonEncoder()]);
$serializer->deserialize('"42"', 'int', 'json');
}
public function testDeserializeScalarArray()
{
$serializer = new Serializer([new ArrayDenormalizer()], ['json' => new JsonEncoder()]);
$this->assertSame([42], $serializer->deserialize('[42]', 'int[]', 'json'));
$this->assertSame([true, false], $serializer->deserialize('[true,false]', 'bool[]', 'json'));
$this->assertSame([3.14, 3.24], $serializer->deserialize('[3.14,32.4e-1]', 'float[]', 'json'));
$this->assertSame([' spaces ', '@Ca$e%'], $serializer->deserialize('[" spaces ","@Ca$e%"]', 'string[]', 'json'));
}
public function testDeserializeInconsistentScalarArray()
{
$this->expectException(NotNormalizableValueException::class);
$serializer = new Serializer([new ArrayDenormalizer()], ['json' => new JsonEncoder()]);
$serializer->deserialize('["42"]', 'int[]', 'json');
}
private function serializerWithClassDiscriminator()
{
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));