From 3959d4fb0d7c2faeb610f924aaa0f6eb2f73acb0 Mon Sep 17 00:00:00 2001 From: Marcin Czarnecki Date: Thu, 4 May 2023 22:37:58 +0200 Subject: [PATCH] feat(handler): Create JsonSerializable handler --- doc/reference/annotations.rst | 16 ++++++++++ src/Handler/JsonSerializableHandler.php | 15 ++++++++++ src/SerializerBuilder.php | 2 ++ .../Benchmark/AbstractSerializationBench.php | 10 ++++++- .../Performance/JsonSerializableBench.php | 24 +++++++++++++++ tests/Fixtures/Author.php | 10 ++++++- tests/Fixtures/ObjectWithJsonSerialisable.php | 21 +++++++++++++ tests/Handler/JsonSerializableHandlerTest.php | 30 +++++++++++++++++++ .../Serializer/BaseSerializationTestCase.php | 7 +++++ tests/Serializer/JsonSerializationTest.php | 8 +++++ 10 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 src/Handler/JsonSerializableHandler.php create mode 100644 tests/Benchmark/Performance/JsonSerializableBench.php create mode 100644 tests/Fixtures/ObjectWithJsonSerialisable.php create mode 100644 tests/Handler/JsonSerializableHandlerTest.php diff --git a/doc/reference/annotations.rst b/doc/reference/annotations.rst index a88c4966f..ef85831f4 100644 --- a/doc/reference/annotations.rst +++ b/doc/reference/annotations.rst @@ -503,6 +503,9 @@ Available Types: | Iterator | Similar to array, but will be deserialized | | | into ArrayIterator class. | +------------------------------------------------------------+--------------------------------------------------+ +| JsonSerializable | Can be used only for serialisation to JSON. | +| | See: JsonSerializable Support section | ++------------------------------------------------------------+--------------------------------------------------+ (*) If the standalone jms/serializer is used then default format is `\DateTime::ISO8601` (which is not compatible with ISO-8601 despite the name). For jms/serializer-bundle the default format is `\DateTime::ATOM` (the real ISO-8601 format) but it can be changed in `configuration`_. @@ -970,3 +973,16 @@ the underlying type using the ``@Type`` annotation. - If the enum is a ``BackedEnum``, the case value will be used for serialization and deserialization by default; - If the enum is not a ``BackedEnum``, the case name will be used for serialization and deserialization by default; + +JsonSerializable Support +~~~~~~~~~~~~~~~~~~~~~~~~ +You can use native `jsonSerialize()` method to serialise your data into JSON. You can use it by Type hinting property as `JsonSerializable`. +You can also register existing Handler for any of your classes. It might simplify your setup of your serialisation and improve performance of serialisation. + +.. code-block :: php + + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, Author::class, 'json', new JsonSerializableHandler()); + + +Keep in mind that returned value from `JsonSerializable::jsonSerialize()` will be serialised directly by `json_encode()` function. +If it will return another object it will be *not* passed to JMS Serializer. diff --git a/src/Handler/JsonSerializableHandler.php b/src/Handler/JsonSerializableHandler.php new file mode 100644 index 000000000..3d8de4762 --- /dev/null +++ b/src/Handler/JsonSerializableHandler.php @@ -0,0 +1,15 @@ +jsonSerialize(); + } +} diff --git a/src/SerializerBuilder.php b/src/SerializerBuilder.php index d54fd1fcc..42920c7bc 100644 --- a/src/SerializerBuilder.php +++ b/src/SerializerBuilder.php @@ -37,6 +37,7 @@ use JMS\Serializer\Handler\HandlerRegistry; use JMS\Serializer\Handler\HandlerRegistryInterface; use JMS\Serializer\Handler\IteratorHandler; +use JMS\Serializer\Handler\JsonSerializableHandler; use JMS\Serializer\Handler\StdClassHandler; use JMS\Serializer\Naming\CamelCaseNamingStrategy; use JMS\Serializer\Naming\PropertyNamingStrategyInterface; @@ -278,6 +279,7 @@ public function addDefaultHandlers(): self $this->handlerRegistry->registerSubscribingHandler(new StdClassHandler()); $this->handlerRegistry->registerSubscribingHandler(new ArrayCollectionHandler()); $this->handlerRegistry->registerSubscribingHandler(new IteratorHandler()); + $this->handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, \JsonSerializable::class, 'json', new JsonSerializableHandler()); if ($this->enableEnumSupport) { $this->handlerRegistry->registerSubscribingHandler(new EnumHandler()); diff --git a/tests/Benchmark/AbstractSerializationBench.php b/tests/Benchmark/AbstractSerializationBench.php index c0fd5dbf8..510151ebc 100644 --- a/tests/Benchmark/AbstractSerializationBench.php +++ b/tests/Benchmark/AbstractSerializationBench.php @@ -4,6 +4,7 @@ namespace JMS\Serializer\Tests\Benchmark; +use JMS\Serializer\Handler\HandlerRegistryInterface; use JMS\Serializer\SerializationContext; use JMS\Serializer\Serializer; use JMS\Serializer\SerializerBuilder; @@ -46,7 +47,10 @@ abstract class AbstractSerializationBench public function __construct() { - $this->serializer = SerializerBuilder::create()->build(); + $this->serializer = SerializerBuilder::create() + ->configureHandlers(\Closure::fromCallable([$this, 'configureHandlers'])) + ->addDefaultHandlers() + ->build(); $this->collection = $this->createCollection(); $this->format = $this->getFormat(); } @@ -89,4 +93,8 @@ private function createPost() return $post; } + + protected function configureHandlers(HandlerRegistryInterface $handlerRegistry) + { + } } diff --git a/tests/Benchmark/Performance/JsonSerializableBench.php b/tests/Benchmark/Performance/JsonSerializableBench.php new file mode 100644 index 000000000..06922dd0b --- /dev/null +++ b/tests/Benchmark/Performance/JsonSerializableBench.php @@ -0,0 +1,24 @@ +registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, Author::class, 'json', new JsonSerializableHandler()); + } +} diff --git a/tests/Fixtures/Author.php b/tests/Fixtures/Author.php index 9d23520b2..97c3b926d 100644 --- a/tests/Fixtures/Author.php +++ b/tests/Fixtures/Author.php @@ -7,7 +7,7 @@ use JMS\Serializer\Annotation\SerializedName; use JMS\Serializer\Annotation\Type; -class Author +class Author implements \JsonSerializable { /** * @Type("string") @@ -26,4 +26,12 @@ public function getName() { return $this->name; } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return [ + 'json_full_name' => $this->name, + ]; + } } diff --git a/tests/Fixtures/ObjectWithJsonSerialisable.php b/tests/Fixtures/ObjectWithJsonSerialisable.php new file mode 100644 index 000000000..9b3c41511 --- /dev/null +++ b/tests/Fixtures/ObjectWithJsonSerialisable.php @@ -0,0 +1,21 @@ +author = $author; + } +} diff --git a/tests/Handler/JsonSerializableHandlerTest.php b/tests/Handler/JsonSerializableHandlerTest.php new file mode 100644 index 000000000..c2518b400 --- /dev/null +++ b/tests/Handler/JsonSerializableHandlerTest.php @@ -0,0 +1,30 @@ +handler)($this->createMock(SerializationVisitorInterface::class), $data); + + $this->assertEquals(['json_full_name' => 'scyzoryck'], $serialized); + } + + protected function setUp(): void + { + $this->handler = new JsonSerializableHandler(); + } +} diff --git a/tests/Serializer/BaseSerializationTestCase.php b/tests/Serializer/BaseSerializationTestCase.php index c30ba90af..bd3bd5ed1 100644 --- a/tests/Serializer/BaseSerializationTestCase.php +++ b/tests/Serializer/BaseSerializationTestCase.php @@ -28,6 +28,7 @@ use JMS\Serializer\Handler\HandlerRegistry; use JMS\Serializer\Handler\HandlerRegistryInterface; use JMS\Serializer\Handler\IteratorHandler; +use JMS\Serializer\Handler\JsonSerializableHandler; use JMS\Serializer\Handler\StdClassHandler; use JMS\Serializer\Handler\SymfonyUidHandler; use JMS\Serializer\Metadata\Driver\TypedPropertiesDriver; @@ -2138,6 +2139,12 @@ protected function setUp(): void $this->handlerRegistry->registerSubscribingHandler(new IteratorHandler()); $this->handlerRegistry->registerSubscribingHandler(new SymfonyUidHandler()); $this->handlerRegistry->registerSubscribingHandler(new EnumHandler()); + $this->handlerRegistry->registerHandler( + GraphNavigatorInterface::DIRECTION_SERIALIZATION, + \JsonSerializable::class, + 'json', + new JsonSerializableHandler() + ); $this->handlerRegistry->registerHandler( GraphNavigatorInterface::DIRECTION_SERIALIZATION, 'AuthorList', diff --git a/tests/Serializer/JsonSerializationTest.php b/tests/Serializer/JsonSerializationTest.php index 2a79cf3eb..6945686e0 100644 --- a/tests/Serializer/JsonSerializationTest.php +++ b/tests/Serializer/JsonSerializationTest.php @@ -16,6 +16,7 @@ use JMS\Serializer\Tests\Fixtures\FirstClassMapCollection; use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyArrayAndHash; use JMS\Serializer\Tests\Fixtures\ObjectWithInlineArray; +use JMS\Serializer\Tests\Fixtures\ObjectWithJsonSerialisable; use JMS\Serializer\Tests\Fixtures\Tag; use JMS\Serializer\Visitor\Factory\JsonSerializationVisitorFactory; use JMS\Serializer\Visitor\SerializationVisitorInterface; @@ -336,6 +337,13 @@ public function testInlineArray() self::assertEquals($object, $this->deserialize($serialized, ObjectWithInlineArray::class)); } + public function testJsonSerialisable() + { + $object = new ObjectWithJsonSerialisable(new Author('Mickiewicz')); + $serialized = $this->serialize($object); + self::assertEquals('{"author":{"json_full_name":"Mickiewicz"}}', $serialized); + } + public function testSerializeRootArrayWithDefinedKeys() { $author1 = new Author('Jim');