Skip to content

Commit

Permalink
feat(handler): Create JsonSerializable handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcin Czarnecki committed Jun 24, 2023
1 parent b4e6128 commit 3959d4f
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 2 deletions.
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

0 comments on commit 3959d4f

Please sign in to comment.