From 070d654245fbffb9794084089ac9c1d107d3b2e4 Mon Sep 17 00:00:00 2001 From: slava-v Date: Sun, 25 Oct 2020 21:55:54 +0100 Subject: [PATCH] Added default_skip_when_empty option as config using the exclusionStrategy approach Updated documentation Fixed PHPCS warnings --- doc/configuration.rst | 20 +++++++++++ src/Context.php | 16 +++++++++ .../SkipWhenEmptyExclusionStrategy.php | 25 +++++++++++++ ...bjectWithEmptyArrayAndHashNotAnnotated.php | 26 ++++++++++++++ tests/Serializer/BaseSerializationTest.php | 20 +++++++++++ tests/Serializer/JsonSerializationTest.php | 36 +++++++++++++++++++ ...efault_skip_when_empty_disabled_object.xml | 9 +++++ ...default_skip_when_empty_enabled_object.xml | 2 ++ 8 files changed, 154 insertions(+) create mode 100644 src/Exclusion/SkipWhenEmptyExclusionStrategy.php create mode 100644 tests/Fixtures/ObjectWithEmptyArrayAndHashNotAnnotated.php create mode 100644 tests/Serializer/xml/default_skip_when_empty_disabled_object.xml create mode 100644 tests/Serializer/xml/default_skip_when_empty_enabled_object.xml diff --git a/doc/configuration.rst b/doc/configuration.rst index aabe95662..5bd06e2ee 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -104,3 +104,23 @@ a serialization context from your callable and use it. You can also set a default DeserializationContextFactory with ``->setDeserializationContextFactory(function () { /* ... */ })`` to be used with methods ``deserialize()`` and ``fromArray()``. + +Setting a default behaviour for skipping properties with empty values - `SkipWhenEmpty`_ annotation +--------------------------------------------------------------------------------------------------- +To avoid to specifying the annotation ``@skipWhenEmpty`` for each property +it is possible to enable this behaviour in configuration by calling ``enableSkipWhenEmpty`` + +Example using with the SerializerBuilder:: + + use JMS\Serializer\SerializationContext; + + $serializer = JMS\Serializer\SerializerBuilder::create() + ->setSerializationContextFactory(function () { + + return SerializationContext::create() + ->enableSkipWhenEmpty(); + + }) + ->build(); + +.. _SkipWhenEmpty: reference/annotations.html#skipwhenempty \ No newline at end of file diff --git a/src/Context.php b/src/Context.php index 36adc2e69..0410eee88 100644 --- a/src/Context.php +++ b/src/Context.php @@ -10,6 +10,7 @@ use JMS\Serializer\Exclusion\DisjunctExclusionStrategy; use JMS\Serializer\Exclusion\ExclusionStrategyInterface; use JMS\Serializer\Exclusion\GroupsExclusionStrategy; +use JMS\Serializer\Exclusion\SkipWhenEmptyExclusionStrategy; use JMS\Serializer\Exclusion\VersionExclusionStrategy; use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\PropertyMetadata; @@ -18,6 +19,7 @@ abstract class Context { + public const ATTR_SKIP_WHEN_EMPTY = 'default_skip_when_empty'; /** * @var array */ @@ -83,6 +85,10 @@ public function initialize(string $format, VisitorInterface $visitor, GraphNavig $this->addExclusionStrategy(new DepthExclusionStrategy()); } + if (!empty($this->attributes[self::ATTR_SKIP_WHEN_EMPTY])) { + $this->addExclusionStrategy(new SkipWhenEmptyExclusionStrategy()); + } + $this->initialized = true; } @@ -204,6 +210,16 @@ public function enableMaxDepthChecks(): self return $this; } + /** + * @return $this + */ + public function enableSkipWhenEmpty(): self + { + $this->attributes[self::ATTR_SKIP_WHEN_EMPTY] = true; + + return $this; + } + public function getFormat(): string { return $this->format; diff --git a/src/Exclusion/SkipWhenEmptyExclusionStrategy.php b/src/Exclusion/SkipWhenEmptyExclusionStrategy.php new file mode 100644 index 000000000..8fa2d92cd --- /dev/null +++ b/src/Exclusion/SkipWhenEmptyExclusionStrategy.php @@ -0,0 +1,25 @@ +skipWhenEmpty + || ($context->hasAttribute(Context::ATTR_SKIP_WHEN_EMPTY) + && $context->getAttribute(Context::ATTR_SKIP_WHEN_EMPTY) + ); + } +} diff --git a/tests/Fixtures/ObjectWithEmptyArrayAndHashNotAnnotated.php b/tests/Fixtures/ObjectWithEmptyArrayAndHashNotAnnotated.php new file mode 100644 index 000000000..b75e75193 --- /dev/null +++ b/tests/Fixtures/ObjectWithEmptyArrayAndHashNotAnnotated.php @@ -0,0 +1,26 @@ +") + */ + private $hash = []; + /** + * @Serializer\Type("array") + */ + private $array = []; + + private $object = []; + + public function __construct() + { + $this->object = new InlineChildEmpty(); + } +} diff --git a/tests/Serializer/BaseSerializationTest.php b/tests/Serializer/BaseSerializationTest.php index 7a92edd42..844ac8900 100644 --- a/tests/Serializer/BaseSerializationTest.php +++ b/tests/Serializer/BaseSerializationTest.php @@ -1627,6 +1627,26 @@ public function testMaxDepthWithSkippableObject() self::assertEquals($this->getContent('maxdepth_skippabe_object'), $serialized); } + public function testSkipeWhenEmptyByDefaultEnabled() + { + $data = new Gh236Foo(); + + $context = SerializationContext::create()->enableSkipWhenEmpty(); + $serialized = $this->serialize($data, $context); + + self::assertEquals($this->getContent('default_skip_when_empty_enabled_object'), $serialized); + } + + public function testSkipWhenEmptyByDefaultDisabled() + { + $data = new Gh236Foo(); + + $context = SerializationContext::create(); + $serialized = $this->serialize($data, $context); + + self::assertEquals($this->getContent('default_skip_when_empty_disabled_object'), $serialized); + } + public function testDeserializingIntoExistingObject() { if (!$this->hasDeserializer()) { diff --git a/tests/Serializer/JsonSerializationTest.php b/tests/Serializer/JsonSerializationTest.php index 23b2d36bb..ae4092df5 100644 --- a/tests/Serializer/JsonSerializationTest.php +++ b/tests/Serializer/JsonSerializationTest.php @@ -14,6 +14,7 @@ use JMS\Serializer\Tests\Fixtures\AuthorList; use JMS\Serializer\Tests\Fixtures\FirstClassMapCollection; use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyArrayAndHash; +use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyArrayAndHashNotAnnotated; use JMS\Serializer\Tests\Fixtures\ObjectWithInlineArray; use JMS\Serializer\Tests\Fixtures\Tag; use JMS\Serializer\Visitor\Factory\JsonSerializationVisitorFactory; @@ -111,6 +112,8 @@ protected function getContent($key) $outputs['author_expression'] = '{"my_first_name":"Ruud","last_name":"Kamphuis","id":123}'; $outputs['author_expression_context'] = '{"first_name":"Ruud","direction":1,"name":"name"}'; $outputs['maxdepth_skippabe_object'] = '{"a":{"xxx":"yyy"}}'; + $outputs['default_skip_when_empty_enabled_object'] = '{}'; + $outputs['default_skip_when_empty_disabled_object'] = '{"a":{"xxx":"yyy","inner":{"xxx":"yyy"}}}'; $outputs['array_objects_nullable'] = '[]'; $outputs['type_casting'] = '{"as_string":"8"}'; $outputs['authors_inline'] = '[{"full_name":"foo"},{"full_name":"bar"}]'; @@ -146,6 +149,25 @@ public function testSkipEmptyArrayAndHash() self::assertEquals('{}', $this->serialize($object)); } + /** + * @param bool $flagEnabled + * @param string $expectedSerializedValue + * + * @dataProvider getSkipEmptyArrayAndHashValues + */ + public function testSkipEmptyArrayAndHash1(bool $flagEnabled, string $expectedSerializedValue) + { + $object = new ObjectWithEmptyArrayAndHashNotAnnotated(); + + $context = SerializationContext::create(); + + if ($flagEnabled) { + $context->enableSkipWhenEmpty(); + } + + self::assertEquals($expectedSerializedValue, $this->serialize($object, $context)); + } + public function getFirstClassMapCollectionsValues() { return [ @@ -428,6 +450,20 @@ public function testTypeHintedArrayAndStdClassSerialization(array $array, $expec self::assertEquals($expected, $this->serialize($array, $context)); } + public function getSkipEmptyArrayAndHashValues() + { + return [ + 'default skip_when_empty disabled' => [ + false, + 'expected' => '{"hash":{},"array":[],"object":{}}', + ], + 'default skip_when_empty enabled' => [ + true, + 'expected' => '{}', + ], + ]; + } + protected function getFormat() { return 'json'; diff --git a/tests/Serializer/xml/default_skip_when_empty_disabled_object.xml b/tests/Serializer/xml/default_skip_when_empty_disabled_object.xml new file mode 100644 index 000000000..61a60c308 --- /dev/null +++ b/tests/Serializer/xml/default_skip_when_empty_disabled_object.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/Serializer/xml/default_skip_when_empty_enabled_object.xml b/tests/Serializer/xml/default_skip_when_empty_enabled_object.xml new file mode 100644 index 000000000..a648dc6e1 --- /dev/null +++ b/tests/Serializer/xml/default_skip_when_empty_enabled_object.xml @@ -0,0 +1,2 @@ + +