Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(handler): Create JsonSerializable handler #1486

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions doc/reference/annotations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@ Available Types:
| Iterator<K, V> | Similar to array<K, V>, 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`_.

Expand Down Expand Up @@ -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.
15 changes: 15 additions & 0 deletions src/Handler/JsonSerializableHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace JMS\Serializer\Handler;

use JMS\Serializer\Visitor\SerializationVisitorInterface;

final class JsonSerializableHandler
{
public function __invoke(SerializationVisitorInterface $visitor, \JsonSerializable $object)
{
return $object->jsonSerialize();
}
}
2 changes: 2 additions & 0 deletions src/SerializerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down
10 changes: 9 additions & 1 deletion tests/Benchmark/AbstractSerializationBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -89,4 +93,8 @@ private function createPost()

return $post;
}

protected function configureHandlers(HandlerRegistryInterface $handlerRegistry)
{
}
}
24 changes: 24 additions & 0 deletions tests/Benchmark/Performance/JsonSerializableBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace JMS\Serializer\Tests\Benchmark\Performance;

use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\HandlerRegistryInterface;
use JMS\Serializer\Handler\JsonSerializableHandler;
use JMS\Serializer\Tests\Benchmark\AbstractSerializationBench;
use JMS\Serializer\Tests\Fixtures\Author;

class JsonSerializableBench extends AbstractSerializationBench
{
protected function getFormat(): string
{
return 'json';
}

protected function configureHandlers(HandlerRegistryInterface $handlerRegistry)
{
$handlerRegistry->registerHandler(GraphNavigatorInterface::DIRECTION_SERIALIZATION, Author::class, 'json', new JsonSerializableHandler());
}
}
10 changes: 9 additions & 1 deletion tests/Fixtures/Author.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use JMS\Serializer\Annotation\SerializedName;
use JMS\Serializer\Annotation\Type;

class Author
class Author implements \JsonSerializable
{
/**
* @Type("string")
Expand All @@ -26,4 +26,12 @@ public function getName()
{
return $this->name;
}

#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return [
'json_full_name' => $this->name,
];
}
}
21 changes: 21 additions & 0 deletions tests/Fixtures/ObjectWithJsonSerialisable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace JMS\Serializer\Tests\Fixtures;

use JMS\Serializer\Annotation as Serializer;

final class ObjectWithJsonSerialisable
{
/**
* @Serializer\Type("JsonSerializable")
*/
#[Serializer\Type(name: \JsonSerializable::class)]
public $author;

public function __construct(Author $author)
{
$this->author = $author;
}
}
30 changes: 30 additions & 0 deletions tests/Handler/JsonSerializableHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace JMS\Serializer\Tests\Handler;

use JMS\Serializer\Handler\JsonSerializableHandler;
use JMS\Serializer\Tests\Fixtures\Author;
use JMS\Serializer\Visitor\SerializationVisitorInterface;
use PHPUnit\Framework\TestCase;

final class JsonSerializableHandlerTest extends TestCase
{
/** @var JsonSerializableHandler */
private $handler;

public function testSerialize(): void
{
$data = new Author('scyzoryck');

$serialized = ($this->handler)($this->createMock(SerializationVisitorInterface::class), $data);

$this->assertEquals(['json_full_name' => 'scyzoryck'], $serialized);
}

protected function setUp(): void
{
$this->handler = new JsonSerializableHandler();
}
}
7 changes: 7 additions & 0 deletions tests/Serializer/BaseSerializationTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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',
Expand Down
8 changes: 8 additions & 0 deletions tests/Serializer/JsonSerializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
Expand Down