Skip to content

Commit

Permalink
Custom serialization for specific classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Tino Adams committed Jun 9, 2016
1 parent 68c6a72 commit 5c25ea2
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 5 deletions.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,43 @@ $serialized = $jsonSerializer->serialize($toBeSerialized);

PS: JsonSerializer does not have a hard dependency of SuperClosure. If you want to use both projects
make sure you add both on your composer requirements.

## Custom Serializers

Some classes may not be suited to be serialized and unserialized using the default reflection methods.

Custom serializers provide the ability to define ```serialize``` and ```unserialize``` methods for specific classes.

```php
class MyType {
public $field1;
public $field2;
}

class MyTypeSerializer {
public function serialize(MyType $obj) {
return array('fields' => $obj->field1 . ' ' . $obj->field2);
}

public function unserialize($values) {
list($field1, $field2) = explode(' ', $values['fields']);
$obj = new MyType();
$obj->field1 = $field1;
$obj->field2 = $field2;
return $obj;
}
}

// map of "class name" => Custom serializer
$customObjectSerializers['MyType'] = new MyTypeSerializer();
$jsonSerializer = new Zumba\JsonSerializer\JsonSerializer(null, $customObjectSerializers);

$toBeSerialized = new MyType();
$toBeSerialized->field1 = 'x';
$toBeSerialized->field2 = 'y';
$json = $jsonSerializer->serialize($toBeSerialized);
// $json == {"@type":"Zumba\\\\JsonSerializer\\\\Test\\\\SupportClasses\\\\MyType","fields":"x y"}

$myType = $jsonSerializer->unserialize($json);
// $myType == $toBeSerialized
```
29 changes: 25 additions & 4 deletions src/JsonSerializer/JsonSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,24 @@ class JsonSerializer
*/
protected $closureSerializer;

/**
* Map of custom object serializers
*
* @var array
*/
protected $customObjectSerializerMap;

/**
* Constructor.
*
* @param ClosureSerializerInterface $closureSerializer
* @param array $customObjectSerializerMap
*/
public function __construct(ClosureSerializerInterface $closureSerializer = null)
public function __construct(ClosureSerializerInterface $closureSerializer = null, $customObjectSerializerMap = array())
{
$this->preserveZeroFractionSupport = defined('JSON_PRESERVE_ZERO_FRACTION');
$this->closureSerializer = $closureSerializer;
$this->customObjectSerializerMap = (array)$customObjectSerializerMap;
}

/**
Expand Down Expand Up @@ -167,15 +176,21 @@ protected function serializeData($value)
*/
protected function serializeObject($value)
{
$ref = new ReflectionClass($value);

if ($this->objectStorage->contains($value)) {
return array(static::CLASS_IDENTIFIER_KEY => '@' . $this->objectStorage[$value]);
}
$this->objectStorage->attach($value, $this->objectMappingIndex++);

$ref = new ReflectionClass($value);
$className = $ref->getName();
if (array_key_exists($className, $this->customObjectSerializerMap)) {
$data = array(static::CLASS_IDENTIFIER_KEY => $className);
$data += $this->customObjectSerializerMap[$className]->serialize($value);
return $data;
}

$paramsToSerialize = $this->getObjectProperties($ref, $value);
$data = array(static::CLASS_IDENTIFIER_KEY => $ref->getName());
$data = array(static::CLASS_IDENTIFIER_KEY => $className);
$data += array_map(array($this, 'serializeData'), $this->extractObjectData($value, $ref, $paramsToSerialize));
return $data;
}
Expand Down Expand Up @@ -266,6 +281,12 @@ protected function unserializeObject($value)
return $this->objectMapping[$index];
}

if (array_key_exists($className, $this->customObjectSerializerMap)) {
$obj = $this->customObjectSerializerMap[$className]->unserialize($value);
$this->objectMapping[$this->objectMappingIndex++] = $obj;
return $obj;
}

if (!class_exists($className)) {
throw new JsonSerializerException('Unable to find class ' . $className);
}
Expand Down
31 changes: 30 additions & 1 deletion tests/JsonSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class JsonSerializerTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
parent::setUp();
$this->serializer = new JsonSerializer();
$customObjectSerializerMap['Zumba\\JsonSerializer\\Test\\SupportClasses\\MyType'] = new \Zumba\JsonSerializer\Test\SupportClasses\MyTypeSerializer();
$this->serializer = new JsonSerializer(null, $customObjectSerializerMap);
}

/**
Expand Down Expand Up @@ -228,6 +229,34 @@ public function testUnserializeObjects()
$this->assertInstanceOf('Zumba\JsonSerializer\Test\SupportClasses\EmptyClass', $array['instance']);
}


/**
* Test serialization of objects using the custom serializers
*
* @return void
*/
public function testCustomObjectSerializer()
{
$obj = new SupportClasses\MyType();
$obj->field1 = 'x';
$obj->field2 = 'y';
$this->assertSame('{"@type":"Zumba\\\\JsonSerializer\\\\Test\\\\SupportClasses\\\\MyType","fields":"x y"}', $this->serializer->serialize($obj));
}

/**
* Test unserialization of objects using the custom serializers
*
* @return void
*/
public function testCustomObjectsUnserializer()
{
$serialized = '{"@type":"Zumba\\\\JsonSerializer\\\\Test\\\\SupportClasses\\\\MyType","fields":"x y"}';
$obj = $this->serializer->unserialize($serialized);
$this->assertInstanceOf('Zumba\JsonSerializer\Test\SupportClasses\MyType', $obj);
$this->assertAttributeSame('x', 'field1', $obj);
$this->assertAttributeSame('y', 'field2', $obj);
}

/**
* Test magic serialization methods
*
Expand Down
9 changes: 9 additions & 0 deletions tests/SupportClasses/MyType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Zumba\JsonSerializer\Test\SupportClasses;

class MyType
{
public $field1;
public $field2;
}
20 changes: 20 additions & 0 deletions tests/SupportClasses/MyTypeSerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Zumba\JsonSerializer\Test\SupportClasses;

class MyTypeSerializer
{
public function serialize(MyType $obj)
{
return array('fields' => $obj->field1 . ' ' . $obj->field2);
}

public function unserialize($values)
{
list($field1, $field2) = explode(' ', $values['fields']);
$obj = new MyType();
$obj->field1 = $field1;
$obj->field2 = $field2;
return $obj;
}
}

0 comments on commit 5c25ea2

Please sign in to comment.