diff --git a/README.md b/README.md index 7029d64..986d28d 100644 --- a/README.md +++ b/README.md @@ -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 +``` diff --git a/src/JsonSerializer/JsonSerializer.php b/src/JsonSerializer/JsonSerializer.php index f8dc0d4..860cc9b 100644 --- a/src/JsonSerializer/JsonSerializer.php +++ b/src/JsonSerializer/JsonSerializer.php @@ -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; } /** @@ -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; } @@ -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); } diff --git a/tests/JsonSerializerTest.php b/tests/JsonSerializerTest.php index 57632cd..f753fc3 100644 --- a/tests/JsonSerializerTest.php +++ b/tests/JsonSerializerTest.php @@ -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); } /** @@ -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 * diff --git a/tests/SupportClasses/MyType.php b/tests/SupportClasses/MyType.php new file mode 100644 index 0000000..66bffc0 --- /dev/null +++ b/tests/SupportClasses/MyType.php @@ -0,0 +1,9 @@ + $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; + } +}