diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index f2171cb5f4..ab845f736c 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -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. diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index ee2c750179..8dc1faf5de 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -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); } /** diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 4852721539..a60c81322f 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -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()));