merged branch lsmith77/serializer_interface (PR #2530)

Commits
-------

0776b50 removed supports(De)Serializiation()
72b9083 SerializerAwareNormalizer now only implements SerializerAwareInterface
97389fa use Serializer specific RuntimeException
cb495fd added additional unit tests for deserialization
967531f fixed various typos from the refactoring
067242d updated serializer tests to use the new interfaces
d811e29 CS fix
351eaa8 require a (de)normalizer inside the (de)normalizable interfaces instead of a serializer
c3d6123 re-added supports(de)normalization()
078f7f3 more typo fixes
c3a711d abstract class children should also implement dernormalization
2a6741c typo fix
d021dc8 refactored encoder handling to use the supports*() methods to determine which encoder handles what format
f8e2787 refactored Normalizer interfaces
58bd0f5 refactored the EncoderInterface
b0daf35 split off an EncoderInterface and NormalizerInterface from SerializerInterface

Discussion
----------

[Serializer] split off an EncoderInterface and NormalizerInterface from SerializerInte

Bug fix: no
Feature addition: no
Backwards compatibility break: yes (but not inside a stable API)
Symfony2 tests pass: ![Build Status](https://secure.travis-ci.org/lsmith77/symfony.png?branch=serializer_interface)
Fixes the following tickets: #2153

The purpose is to make it easier for other implementations that only implement parts of the interface due to different underlying approaches like the JMSSerializerBundle.

---------------------------------------------------------------------------

by schmittjoh at 2011/11/01 03:36:17 -0700

Actually, you can keep the current interface and I will just provide an adapter, sth like the following:

```php
<?php

class SymfonyAdapter implements SymfonyInterface
{
    public function __construct(BundleInterface $serializer) { /* ... */ }
    // symfony serializer methods mapped to bundle methods
}
```
I like to provide an adapter instead of implementing the interface directly since the bundle can be used standalone right now, and I don't want to add a dependency on the component just for the sake of the interface.

However, I do not completely see the purpose of the component. When would someone be recommended to use it? Everything the component does, the bundles does at the same level with the same complexity or simplicity (however you want to view that).

---------------------------------------------------------------------------

by lsmith77 at 2011/11/01 03:40:55 -0700

standalone in what way? you mean even out of the context of Symfony? In that context imho you should ship that code outside of a Bundle.

Regardless, how will that adaptor work? How would you implement methods like ``getEncoder()``? Afaik you can't and this is what this PR is about, splitting the interface to enable people to more finely specify what they provide.

---------------------------------------------------------------------------

by schmittjoh at 2011/11/01 04:03:56 -0700

I would just throw exceptions when something is not supported.

The more important question though is what is the goal of the component in the long-term, i.e. what problems is it supposed to solve, or in which cases should it be used?

Because right now it seems to me - correct me if I'm wrong - that the only purpose is that people don't have to install an extra library. However, that might even be frustrating for users because they need to migrate their code to the bundle as soon as they need to customize the serialization process which you need in 99% of the cases. For deserialization, the situation in the component is even worse. So, if my assessment is correct here (i.e. component to get started fast, if you need more migrate to the bundle), I think it would be better and less painful to have them start with the bundle right away.

---------------------------------------------------------------------------

by lsmith77 at 2011/11/01 04:15:10 -0700

Well then imho it would be better to split the interface.

I think the serializer component is sufficient for many situations and imho its easier to grok. Furthermore the normalizer/encoder concept it can be used in situations where JMSSerializerBundle cannot be used.

And splitting up the interfaces has exactly the goal of reducing the "frustrations" caused by out growing the the component.

---------------------------------------------------------------------------

by schmittjoh at 2011/11/01 04:29:39 -0700

I don't agree, but it's a subjective thing anyway.

So, whatever interface you come up with (preferably as few methods as possible), I will provide an adapter for it.

---------------------------------------------------------------------------

by fabpot at 2011/11/07 08:45:25 -0800

What's the status of this PR?

---------------------------------------------------------------------------

by lsmith77 at 2011/11/07 10:28:14 -0800

from my POV its good to go. but would like a nod from someone else in terms of the naming of the new interfaces

On 07.11.2011, at 17:45, Fabien Potencier <reply@reply.github.com> wrote:

> What's the status of this PR?
>
> ---
> Reply to this email directly or view it on GitHub:
> https://github.com/symfony/symfony/pull/2530#issuecomment-2655889

---------------------------------------------------------------------------

by stof at 2011/11/08 11:37:40 -0800

@lsmith77 what about doing the same for the ``NormalizerInterface`` instead of adding a new interface with a confusing name ? The Serializer class could implement ``Normalizer\NormalizerInterface`` by adding the 2 needed methods instead of duplicating part of the interface.

The next step is to refactor the Serializer class so that it choose the encoder and the decoder based on the ``support*`` methods. But this could probably be done in a separate PR.

---------------------------------------------------------------------------

by lsmith77 at 2011/11/08 11:51:27 -0800

yeah .. i wanted to do that once we are in agreement on the encoder stuff. question then is if we should again split off Denormalization. i guess yes.

---------------------------------------------------------------------------

by lsmith77 at 2011/11/08 12:06:34 -0800

ok done ..

---------------------------------------------------------------------------

by lsmith77 at 2011/11/08 12:59:51 -0800

i guess the next big task is to add more tests .. had to fix way too few unit tests with all this shuffling around .. will also help validating the concept. i should also test this out in a production application.

---------------------------------------------------------------------------

by lsmith77 at 2011/11/14 13:27:48 -0800

@ericclemmons can you also have a look at this PR and potentially help me adding tests?

---------------------------------------------------------------------------

by fabpot at 2011/12/07 07:32:06 -0800

@lsmith77: Is it ready to be merged? Should I wait for more unit tests?

---------------------------------------------------------------------------

by lsmith77 at 2011/12/07 07:34:56 -0800

If you merge it I am afraid I might get lazy and not write tests. This is why I changed the topic to WIP. I promise to finish this on the weekend.

Note however I was planning to write the tests for 2.0 and send them via a separate PR.
Once that PR is merged into 2.0 and master. I would then refactor them to work for this PR.
This way both 2.0 and master will have tests.

---------------------------------------------------------------------------

by fabpot at 2011/12/07 07:42:15 -0800

@lsmith77: sounds good. Thanks.

---------------------------------------------------------------------------

by lsmith77 at 2011/12/11 12:02:12 -0800

@fabpot ok i am done from my end.
@schmittjoh would be great if you could look over the final interfaces one time and give your blessing that you will indeed be able to provide implementations for these interfaces inside JMSSerializerBundle (even if just via an adapter)

---------------------------------------------------------------------------

by stof at 2011/12/12 12:43:49 -0800

@schmittjoh can you take a look as requested by @lsmith77 ?

---------------------------------------------------------------------------

by schmittjoh at 2011/12/13 03:33:23 -0800

Are the supports methods necessary? This is what I'm using in the bundle:
https://github.com/schmittjoh/JMSSerializerBundle/blob/master/Serializer/SerializerInterface.php

---------------------------------------------------------------------------

by lsmith77 at 2011/12/13 04:08:49 -0800

@schmittjoh without them determining if something is supported will always require an exception, which is pretty expensive. especially if one iterates over a data structure this can cause a lot of overhead.

---------------------------------------------------------------------------

by schmittjoh at 2011/12/13 04:24:18 -0800

my question was more if you have a real-world use case where this is useful?

On Tue, Dec 13, 2011 at 1:08 PM, Lukas Kahwe Smith <
reply@reply.github.com
> wrote:

> @schmittjoh without them determining if something is supported will always
> require an exception, which is pretty expensive. especially if one iterates
> over a data structure this can cause a lot of overhead.
>
> ---
> Reply to this email directly or view it on GitHub:
> https://github.com/symfony/symfony/pull/2530#issuecomment-3122157
>

---------------------------------------------------------------------------

by lsmith77 at 2011/12/13 04:28:08 -0800

yes .. this serializer .. since it traverses the tree and decides what is the current normalizer one by one (aka not via visitors as in your implementation). without the supports*() methods it would need to have the normalizer throw exceptions, but this is not exceptional, its the normal code flow to have to iterate to find the correct normalizer.

---------------------------------------------------------------------------

by schmittjoh at 2011/12/13 04:30:36 -0800

can we split it off into a second interface?

On Tue, Dec 13, 2011 at 1:28 PM, Lukas Kahwe Smith <
reply@reply.github.com
> wrote:

> yes .. this serializer .. since it traverses the tree and decides what is
> the current normalizer one by one (aka not via visitors as in your
> implementation). without the supports*() methods it would need to have the
> normalizer throw exceptions, but this is not exceptional, its the normal
> code flow to have to iterate to find the correct normalizer.
>
> ---
> Reply to this email directly or view it on GitHub:
> https://github.com/symfony/symfony/pull/2530#issuecomment-3122315
>

---------------------------------------------------------------------------

by lsmith77 at 2011/12/13 04:33:27 -0800

hmm .. i guess we could .. these methods in a way are implementation specific and are mainly public because its different objects interacting with each other, though for users of the lib they can also be convenient at times.

---------------------------------------------------------------------------

by lsmith77 at 2011/12/14 09:13:53 -0800

ok i reviewed things again and just removed those two methods, since the possibility for these methods to be feasible is too tied to the implementation and for this particular implementation supportsEncoding() and supportsDecoding() are still available.

so all ready to be merged ..

---------------------------------------------------------------------------

by lsmith77 at 2011/12/14 09:15:44 -0800

hmm i realized one thing just now:
cb495fd7a3

that commit should also be included in 2.0 .. i am not sure what the most elegant way is to make that happen ..

---------------------------------------------------------------------------

by fabpot at 2011/12/14 10:10:16 -0800

@lsmith77: commit cb495fd7a3 cannot be cherry picked in 2.0 as is as the tests do not pass:  "Fatal error: Call to undefined method Symfony\Component\Serializer\Serializer::supportsDenormalization() in tests/Symfony/Tests/Component/Serializer/SerializerTest.php on line 150"

---------------------------------------------------------------------------

by lsmith77 at 2011/12/14 10:11:55 -0800

ah of course .. i just removed that method :) .. then never mind .. all is well.
This commit is contained in:
Fabien Potencier 2011-12-14 19:34:07 +01:00
commit b7c7ed4791
18 changed files with 308 additions and 194 deletions

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\Serializer\Encoder;
*/
/**
* Defines the interface of encoders that are able to decode their own format
* Defines the interface of decoders
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
@ -22,10 +22,18 @@ interface DecoderInterface
/**
* Decodes a string into PHP data
*
* @param string $data Data to decode
* @param string $format Format to decode from
* @param scalar $data Data to decode
* @param string $format Format name
*
* @return mixed
*/
function decode($data, $format);
/**
* Checks whether the serializer can decode from given format
*
* @param string $format format name
* @return Boolean
*/
function supportsDecoding($format);
}

View File

@ -2,7 +2,6 @@
namespace Symfony\Component\Serializer\Encoder;
/*
* This file is part of the Symfony framework.
*
@ -20,12 +19,20 @@ namespace Symfony\Component\Serializer\Encoder;
interface EncoderInterface
{
/**
* Encodes data into a string
* Encodes data into the given format
*
* @param mixed $data Data to encode
* @param string $format Format to encode to
* @param string $format Format name
*
* @return string
* @return scalar
*/
function encode($data, $format);
/**
* Checks whether the serializer can encode to given format
*
* @param string $format format name
* @return Boolean
*/
function supportsEncoding($format);
}

View File

@ -34,4 +34,26 @@ class JsonEncoder implements EncoderInterface, DecoderInterface
{
return json_decode($data, true);
}
/**
* Checks whether the serializer can encode to given format
*
* @param string $format format name
* @return Boolean
*/
public function supportsEncoding($format)
{
return 'json' === $format;
}
/**
* Checks whether the serializer can decode from given format
*
* @param string $format format name
* @return Boolean
*/
public function supportsDecoding($format)
{
return 'json' === $format;
}
}

View File

@ -71,6 +71,28 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
return $this->parseXml($xml);
}
/**
* Checks whether the serializer can encode to given format
*
* @param string $format format name
* @return Boolean
*/
public function supportsEncoding($format)
{
return 'xml' === $format;
}
/**
* Checks whether the serializer can decode from given format
*
* @param string $format format name
* @return Boolean
*/
public function supportsDecoding($format)
{
return 'xml' === $format;
}
/**
* Sets the root node name
* @param string $name root node name

View File

@ -15,7 +15,7 @@ namespace Symfony\Component\Serializer\Normalizer;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class CustomNormalizer extends SerializerAwareNormalizer
class CustomNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
/**
* {@inheritdoc}

View File

@ -0,0 +1,37 @@
<?php
namespace Symfony\Component\Serializer\Normalizer;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Defines the most basic interface a class must implement to be denormalizable
*
* If a denormalizer is registered for the class and it doesn't implement
* the Denormalizable interfaces, the normalizer will be used instead
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface DenormalizableInterface
{
/**
* Denormalizes the object back from an array of scalars|arrays.
*
* It is important to understand that the normalize() call should denormalize
* recursively all child objects of the implementor.
*
* @param DenormalizerInterface $denormalizer The denormalizer is given so that you
* can use it to denormalize objects contained within this object.
* @param array|scalar $data The data from which to re-create the object.
* @param string|null $format The format is optionally given to be able to denormalize differently
* based on different input formats.
*/
function denormalize(DenormalizerInterface $denormalizer, $data, $format = null);
}

View File

@ -0,0 +1,40 @@
<?php
namespace Symfony\Component\Serializer\Normalizer;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Defines the interface of denormalizers.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface DenormalizerInterface
{
/**
* Denormalizes data back into an object of the given class
*
* @param mixed $data data to restore
* @param string $class the expected class to instantiate
* @param string $format format the given data was extracted from
* @return object
*/
function denormalize($data, $class, $format = null);
/**
* Checks whether the given class is supported for denormalization by this normalizer
*
* @param mixed $data Data to denormalize from.
* @param string $type The class to which the data should be denormalized.
* @param string $format The format being deserialized from.
* @return Boolean
*/
function supportsDenormalization($data, $type, $format = null);
}

View File

@ -33,7 +33,7 @@ use Symfony\Component\Serializer\Exception\RuntimeException;
*
* @author Nils Adermann <naderman@naderman.de>
*/
class GetSetMethodNormalizer extends SerializerAwareNormalizer
class GetSetMethodNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
/**
* {@inheritdoc}

View File

@ -2,8 +2,6 @@
namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\Serializer\SerializerInterface;
/*
* This file is part of the Symfony framework.
*
@ -29,25 +27,11 @@ interface NormalizableInterface
* It is important to understand that the normalize() call should normalize
* recursively all child objects of the implementor.
*
* @param SerializerInterface $serializer The serializer is given so that you
* @param NormalizerInterface $normalizer The normalizer is given so that you
* can use it to normalize objects contained within this object.
* @param string|null $format The format is optionally given to be able to normalize differently
* based on different output formats.
* @return array|scalar
*/
function normalize(SerializerInterface $serializer, $format = null);
/**
* Denormalizes the object back from an array of scalars|arrays.
*
* It is important to understand that the normalize() call should denormalize
* recursively all child objects of the implementor.
*
* @param SerializerInterface $serializer The serializer is given so that you
* can use it to denormalize objects contained within this object.
* @param array|scalar $data The data from which to re-create the object.
* @param string|null $format The format is optionally given to be able to denormalize differently
* based on different input formats.
*/
function denormalize(SerializerInterface $serializer, $data, $format = null);
function normalize(NormalizerInterface $normalizer, $format = null);
}

View File

@ -2,7 +2,6 @@
namespace Symfony\Component\Serializer\Normalizer;
/*
* This file is part of the Symfony framework.
*
@ -28,16 +27,6 @@ interface NormalizerInterface
*/
function normalize($object, $format = null);
/**
* Denormalizes data back into an object of the given class
*
* @param mixed $data data to restore
* @param string $class the expected class to instantiate
* @param string $format format the given data was extracted from
* @return object
*/
function denormalize($data, $class, $format = null);
/**
* Checks whether the given class is supported for normalization by this normalizer
*
@ -46,14 +35,4 @@ interface NormalizerInterface
* @return Boolean
*/
function supportsNormalization($data, $format = null);
/**
* Checks whether the given class is supported for denormalization by this normalizer
*
* @param mixed $data Data to denormalize from.
* @param string $type The class to which the data should be denormalized.
* @param string $format The format being deserialized from.
* @return Boolean
*/
function supportsDenormalization($data, $type, $format = null);
}

View File

@ -19,7 +19,7 @@ use Symfony\Component\Serializer\SerializerAwareInterface;
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
abstract class SerializerAwareNormalizer implements SerializerAwareInterface, NormalizerInterface
abstract class SerializerAwareNormalizer implements SerializerAwareInterface
{
protected $serializer;

View File

@ -5,6 +5,8 @@ namespace Symfony\Component\Serializer;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@ -32,12 +34,14 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException;
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
*/
class Serializer implements SerializerInterface
class Serializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface, EncoderInterface, DecoderInterface
{
protected $normalizers = array();
protected $encoders = array();
protected $normalizerCache = array();
protected $denormalizerCache = array();
protected $encoderByFormat = array();
protected $decoderByFormat = array();
public function __construct(array $normalizers = array(), array $encoders = array())
{
@ -61,7 +65,7 @@ class Serializer implements SerializerInterface
*/
public final function serialize($data, $format)
{
if (!$this->supportsSerialization($format)) {
if (!$this->supportsEncoding($format)) {
throw new UnexpectedValueException('Serialization for the format '.$format.' is not supported');
}
@ -79,7 +83,7 @@ class Serializer implements SerializerInterface
*/
public final function deserialize($data, $type, $format)
{
if (!$this->supportsDeserialization($format)) {
if (!$this->supportsDecoding($format)) {
throw new UnexpectedValueException('Deserialization for the format '.$format.' is not supported');
}
@ -128,6 +132,66 @@ class Serializer implements SerializerInterface
return $this->denormalizeObject($data, $type, $format);
}
/**
* {@inheritdoc}
*/
public function supportsNormalization($data, $format = null)
{
try {
$this->getNormalizer($data, $format);
} catch (RuntimeException $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
{
try {
$this->getDenormalizer($data, $type, $format = null);
} catch (RuntimeException $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
private function getNormalizer($data, $format = null)
{
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof NormalizerInterface
&& $normalizer->supportsNormalization($data, $format)
) {
return $normalizer;
}
}
throw new RuntimeException(sprintf('No normalizer found for format "%s".', $format));
}
/**
* {@inheritdoc}
*/
private function getDenormalizer($data, $type, $format = null)
{
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof DenormalizerInterface
&& $normalizer->supportsDenormalization($data, $type, $format)
) {
return $normalizer;
}
}
throw new RuntimeException(sprintf('No denormalizer found for format "%s".', $format));
}
/**
* {@inheritdoc}
*/
@ -158,12 +222,18 @@ class Serializer implements SerializerInterface
}
$class = get_class($object);
// If normalization is supported, cached normalizer will exist
if ($this->supportsNormalization($object, $format)) {
if (isset($this->normalizerCache[$class][$format])) {
return $this->normalizerCache[$class][$format]->normalize($object, $format);
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer->supportsNormalization($object, $format)) {
$this->normalizerCache[$class][$format] = $normalizer;
return $normalizer->normalize($object, $format);
}
}
throw new UnexpectedValueException('Could not normalize object of type '.$class.', no supporting normalizer found.');
}
@ -180,9 +250,11 @@ class Serializer implements SerializerInterface
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
}
if (isset($this->denormalizerCache[$class][$format])) {
return $this->denormalizerCache[$class][$format]->denormalize($data, $class, $format);
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer->supportsDenormalization($data, $class, $format)) {
$this->denormalizerCache[$class][$format] = $normalizer;
@ -190,62 +262,22 @@ class Serializer implements SerializerInterface
return $normalizer->denormalize($data, $class, $format);
}
}
throw new UnexpectedValueException('Could not denormalize object of type '.$class.', no supporting normalizer found.');
}
/**
* Check if normalizer cache or normalizers supports provided object, which will then be cached
*
* @param object $object Object to test for normalization support
* @param string $format Format name, needed for normalizers to pivot on
*/
private function supportsNormalization($object, $format)
{
$class = get_class($object);
if (isset($this->normalizerCache[$class][$format])) {
return true;
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer->supportsNormalization($object, $format)) {
$this->normalizerCache[$class][$format] = $normalizer;
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function supportsSerialization($format)
{
return $this->supportsEncoding($format);
}
/**
* {@inheritdoc}
*/
public function supportsDeserialization($format)
{
return $this->supportsDecoding($format);
}
/**
* {@inheritdoc}
*/
public function supportsEncoding($format)
{
try {
$encoder = $this->getEncoder($format);
} catch (\RuntimeException $e) {
$this->getEncoder($format);
} catch (RuntimeException $e) {
return false;
}
return $encoder instanceof EncoderInterface;
return true;
}
/**
@ -254,23 +286,57 @@ class Serializer implements SerializerInterface
public function supportsDecoding($format)
{
try {
$encoder = $this->getEncoder($format);
} catch (\RuntimeException $e) {
$this->getDecoder($format);
} catch (RuntimeException $e) {
return false;
}
return $encoder instanceof DecoderInterface;
return true;
}
/**
* {@inheritdoc}
*/
public function getEncoder($format)
private function getEncoder($format)
{
if (!isset($this->encoders[$format])) {
throw new RuntimeException(sprintf('No encoder found for format "%s".', $format));
if (isset($this->encoderByFormat[$format])
&& isset($this->encoders[$this->encoderByFormat[$format]])
) {
return $this->encoders[$this->encoderByFormat[$format]];
}
return $this->encoders[$format];
foreach ($this->encoders as $i => $encoder) {
if ($encoder instanceof EncoderInterface
&& $encoder->supportsEncoding($format)
) {
$this->encoderByFormat[$format] = $i;
return $encoder;
}
}
throw new RuntimeException(sprintf('No encoder found for format "%s".', $format));
}
/**
* {@inheritdoc}
*/
private function getDecoder($format)
{
if (isset($this->decoderByFormat[$format])
&& isset($this->encoders[$this->decoderByFormat[$format]])
) {
return $this->encoders[$this->decoderByFormat[$format]];
}
foreach ($this->encoders as $i => $encoder) {
if ($encoder instanceof DecoderInterface
&& $encoder->supportsDecoding($format)
) {
$this->decoderByFormat[$format] = $i;
return $encoder;
}
}
throw new RuntimeException(sprintf('No decoder found for format "%s".', $format));
}
}

View File

@ -37,80 +37,4 @@ interface SerializerInterface
* @param string $format
*/
function deserialize($data, $type, $format);
/**
* Normalizes any data into a set of arrays/scalars
*
* @param mixed $data data to normalize
* @param string $format format name, present to give the option to normalizers to act differently based on formats
* @return array|scalar
*/
function normalize($data, $format = null);
/**
* Denormalizes data into the given type.
*
* @param mixed $data
* @param string $type
* @param string $format
* @return mixed
*/
function denormalize($data, $type, $format = null);
/**
* Encodes data into the given format
*
* @param mixed $data data to encode
* @param string $format format name
* @return array|scalar
*/
function encode($data, $format);
/**
* Decodes a string from the given format back into PHP data
*
* @param string $data data to decode
* @param string $format format name
* @return mixed
*/
function decode($data, $format);
/**
* Checks whether the serializer can serialize to given format
*
* @param string $format format name
* @return Boolean
*/
function supportsSerialization($format);
/**
* Checks whether the serializer can deserialize from given format
*
* @param string $format format name
* @return Boolean
*/
function supportsDeserialization($format);
/**
* Checks whether the serializer can encode to given format
*
* @param string $format format name
* @return Boolean
*/
function supportsEncoding($format);
/**
* Checks whether the serializer can decode from given format
*
* @param string $format format name
* @return Boolean
*/
function supportsDecoding($format);
/**
* Get the encoder for the given format
*
* @return EncoderInterface
*/
function getEncoder($format);
}

View File

@ -25,7 +25,7 @@ class XmlEncoderTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
$this->encoder = new XmlEncoder;
$serializer = new Serializer(array(new CustomNormalizer), array('xml' => new XmlEncoder()));
$serializer = new Serializer(array(new CustomNormalizer()), array('xml' => new XmlEncoder()));
$this->encoder->setSerializer($serializer);
}

View File

@ -3,7 +3,8 @@
namespace Symfony\Tests\Component\Serializer\Fixtures;
use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
class Dummy implements NormalizableInterface
{
@ -12,7 +13,7 @@ class Dummy implements NormalizableInterface
public $baz;
public $qux;
public function normalize(SerializerInterface $serializer, $format = null)
public function normalize(NormalizerInterface $normalizer, $format = null)
{
return array(
'foo' => $this->foo,
@ -22,7 +23,7 @@ class Dummy implements NormalizableInterface
);
}
public function denormalize(SerializerInterface $serializer, $data, $format = null)
public function denormalize(DenormalizerInterface $denormalizer, $data, $format = null)
{
$this->foo = $data['foo'];
$this->bar = $data['bar'];

View File

@ -3,11 +3,12 @@
namespace Symfony\Tests\Component\Serializer\Fixtures;
use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
class NormalizableTraversableDummy extends TraversableDummy implements NormalizableInterface
{
public function normalize(SerializerInterface $serializer, $format = null)
public function normalize(NormalizerInterface $normalizer, $format = null)
{
return array(
'foo' => 'normalizedFoo',
@ -15,7 +16,7 @@ class NormalizableTraversableDummy extends TraversableDummy implements Normaliza
);
}
public function denormalize(SerializerInterface $serializer, $data, $format = null)
public function denormalize(DenormalizerInterface $denormalizer, $data, $format = null)
{
return array(
'foo' => 'denormalizedFoo',

View File

@ -3,19 +3,21 @@
namespace Symfony\Tests\Component\Serializer\Fixtures;
use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizableInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
class ScalarDummy implements NormalizableInterface
class ScalarDummy implements NormalizableInterface, DenormalizableInterface
{
public $foo;
public $xmlFoo;
public function normalize(SerializerInterface $serializer, $format = null)
public function normalize(NormalizerInterface $normalizer, $format = null)
{
return $format === 'xml' ? $this->xmlFoo : $this->foo;
}
public function denormalize(SerializerInterface $serializer, $data, $format = null)
public function denormalize(DenormalizerInterface $denormalizer, $data, $format = null)
{
if ($format === 'xml') {
$this->xmlFoo = $data;

View File

@ -136,13 +136,34 @@ class SerializerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException
*/
public function testDeerializeNoEncoder()
public function testDeserializeNoEncoder()
{
$this->serializer = new Serializer(array(), array());
$data = array('title' => 'foo', 'numbers' => array(5, 3));
$this->serializer->deserialize(json_encode($data), '\Symfony\Tests\Component\Serializer\Model', 'json');
}
public function testDeserializeSupported()
{
$this->serializer = new Serializer(array(new GetSetMethodNormalizer()), array());
$data = array('title' => 'foo', 'numbers' => array(5, 3));
$this->assertTrue($this->serializer->supportsDenormalization(json_encode($data), '\Symfony\Tests\Component\Serializer\Model', 'json'));
}
public function testDeserializeNotSupported()
{
$this->serializer = new Serializer(array(new GetSetMethodNormalizer()), array());
$data = array('title' => 'foo', 'numbers' => array(5, 3));
$this->assertFalse($this->serializer->supportsDenormalization(json_encode($data), 'stdClass', 'json'));
}
public function testDeserializeNotSupportedMissing()
{
$this->serializer = new Serializer(array(), array());
$data = array('title' => 'foo', 'numbers' => array(5, 3));
$this->assertFalse($this->serializer->supportsDenormalization(json_encode($data), '\Symfony\Tests\Component\Serializer\Model', 'json'));
}
public function testEncode()
{
$this->serializer = new Serializer(array(), array('json' => new JsonEncoder()));