diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 97ae3fd62fdab..e652194340038 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * added `FormErrorNormalizer` * added `MimeMessageNormalizer` * serializer mapping can be configured using php attributes + * added `skip_uninitialized_properties` context option to not serialize uninitialized properties 5.1.0 ----- diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index aa1be48cfbaf5..866d3f72c9962 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -13,6 +13,7 @@ use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Encoder\CsvEncoder; @@ -58,6 +59,12 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer */ public const SKIP_NULL_VALUES = 'skip_null_values'; + /** + * Flag to control whether uninitialized fields should be skipped + * when normalizing. + */ + public const SKIP_UNINITIALIZED_PROPERTIES = 'skip_uninitialized_properties'; + /** * Callback to allow to set a value for an attribute when the max depth has * been reached. @@ -175,7 +182,17 @@ public function normalize($object, string $format = null, array $context = []) continue; } - $attributeValue = $this->getAttributeValue($object, $attribute, $format, $context); + $attributeValue = null; + try { + $attributeValue = $this->getAttributeValue($object, $attribute, $format, $context); + } catch (UninitializedPropertyException $e) { + if (isset($context[self::SKIP_UNINITIALIZED_PROPERTIES])) { + continue; + } + + throw $e; + } + if ($maxDepthReached) { $attributeValue = $maxDepthHandler($attributeValue, $object, $attribute, $format, $context); } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy2.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy2.php new file mode 100644 index 0000000000000..2abf7a4b1cc58 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummy2.php @@ -0,0 +1,16 @@ +bar = 'present'; + + $normalizer = $this->getNormalizerForSkipUninitializedProperties(); + $result = $normalizer->normalize($dummy, null, ['skip_uninitialized_properties' => true]); + $this->assertSame(['bar' => 'present'], $result); + } + + public function testWithoutSkipUninitializedProperties() + { + $this->expectException(UninitializedPropertyException::class); + + $normalizer = $this->getNormalizerForSkipUninitializedProperties(); + $normalizer->normalize(new ObjectDummy2(), null); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 66d578f1ebf85..5094e01e0bfbd 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -28,8 +28,8 @@ use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\SerializerInterface; -use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\GroupDummy; +use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy; use Symfony\Component\Serializer\Tests\Fixtures\OtherSerializedNameDummy; use Symfony\Component\Serializer\Tests\Fixtures\Php74Dummy; use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder; @@ -43,6 +43,7 @@ use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectDummy; use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectToPopulateTestTrait; use Symfony\Component\Serializer\Tests\Normalizer\Features\SkipNullValuesTestTrait; +use Symfony\Component\Serializer\Tests\Normalizer\Features\SkipUninitializedPropertiesTestTrait; use Symfony\Component\Serializer\Tests\Normalizer\Features\TypeEnforcementTestTrait; /** @@ -59,6 +60,7 @@ class ObjectNormalizerTest extends TestCase use MaxDepthTestTrait; use ObjectToPopulateTestTrait; use SkipNullValuesTestTrait; + use SkipUninitializedPropertiesTestTrait; use TypeEnforcementTestTrait; /** @@ -506,6 +508,13 @@ protected function getNormalizerForSkipNullValues(): ObjectNormalizer return new ObjectNormalizer(); } + // skip uninitialized + + protected function getNormalizerForSkipUninitializedProperties(): ObjectNormalizer + { + return new ObjectNormalizer(); + } + // type enforcement protected function getDenormalizerForTypeEnforcement(): ObjectNormalizer