4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Serializer;
14 use Symfony\Component\Serializer\Encoder\ChainDecoder;
15 use Symfony\Component\Serializer\Encoder\ChainEncoder;
16 use Symfony\Component\Serializer\Encoder\DecoderInterface;
17 use Symfony\Component\Serializer\Encoder\EncoderInterface;
18 use Symfony\Component\Serializer\Exception\LogicException;
19 use Symfony\Component\Serializer\Exception\NotEncodableValueException;
20 use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
21 use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
22 use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
23 use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
24 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
27 * Serializer serializes and deserializes data.
29 * objects are turned into arrays by normalizers.
30 * arrays are turned into various output formats by encoders.
32 * $serializer->serialize($obj, 'xml')
33 * $serializer->decode($data, 'xml')
34 * $serializer->denormalize($data, 'Class', 'xml')
36 * @author Jordi Boggiano <j.boggiano@seld.be>
37 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
38 * @author Lukas Kahwe Smith <smith@pooteeweet.org>
39 * @author Kévin Dunglas <dunglas@gmail.com>
41 class Serializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface, EncoderInterface, DecoderInterface
44 * @var Encoder\ChainEncoder
49 * @var Encoder\ChainDecoder
56 protected $normalizers = array();
61 * @deprecated since 3.1 will be removed in 4.0
63 protected $normalizerCache = array();
68 * @deprecated since 3.1 will be removed in 4.0
70 protected $denormalizerCache = array();
72 public function __construct(array $normalizers = array(), array $encoders = array())
74 foreach ($normalizers as $normalizer) {
75 if ($normalizer instanceof SerializerAwareInterface) {
76 $normalizer->setSerializer($this);
79 if ($normalizer instanceof DenormalizerAwareInterface) {
80 $normalizer->setDenormalizer($this);
83 if ($normalizer instanceof NormalizerAwareInterface) {
84 $normalizer->setNormalizer($this);
87 $this->normalizers = $normalizers;
90 $realEncoders = array();
91 foreach ($encoders as $encoder) {
92 if ($encoder instanceof SerializerAwareInterface) {
93 $encoder->setSerializer($this);
95 if ($encoder instanceof DecoderInterface) {
96 $decoders[] = $encoder;
98 if ($encoder instanceof EncoderInterface) {
99 $realEncoders[] = $encoder;
102 $this->encoder = new ChainEncoder($realEncoders);
103 $this->decoder = new ChainDecoder($decoders);
109 final public function serialize($data, $format, array $context = array())
111 if (!$this->supportsEncoding($format, $context)) {
112 throw new NotEncodableValueException(sprintf('Serialization for the format %s is not supported', $format));
115 if ($this->encoder->needsNormalization($format, $context)) {
116 $data = $this->normalize($data, $format, $context);
119 return $this->encode($data, $format, $context);
125 final public function deserialize($data, $type, $format, array $context = array())
127 if (!$this->supportsDecoding($format, $context)) {
128 throw new NotEncodableValueException(sprintf('Deserialization for the format %s is not supported', $format));
131 $data = $this->decode($data, $format, $context);
133 return $this->denormalize($data, $type, $format, $context);
139 public function normalize($data, $format = null, array $context = array())
141 // If a normalizer supports the given data, use it
142 if ($normalizer = $this->getNormalizer($data, $format, $context)) {
143 return $normalizer->normalize($data, $format, $context);
146 if (null === $data || is_scalar($data)) {
150 if (\is_array($data) || $data instanceof \Traversable) {
151 $normalized = array();
152 foreach ($data as $key => $val) {
153 $normalized[$key] = $this->normalize($val, $format, $context);
159 if (\is_object($data)) {
160 if (!$this->normalizers) {
161 throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
164 throw new NotNormalizableValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', \get_class($data)));
167 throw new NotNormalizableValueException(sprintf('An unexpected value could not be normalized: %s', var_export($data, true)));
173 * @throws NotNormalizableValueException
175 public function denormalize($data, $type, $format = null, array $context = array())
177 if (!$this->normalizers) {
178 throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
181 if ($normalizer = $this->getDenormalizer($data, $type, $format, $context)) {
182 return $normalizer->denormalize($data, $type, $format, $context);
185 throw new NotNormalizableValueException(sprintf('Could not denormalize object of type %s, no supporting normalizer found.', $type));
191 public function supportsNormalization($data, $format = null/*, array $context = array()*/)
193 if (\func_num_args() > 2) {
194 $context = \func_get_arg(2);
196 if (__CLASS__ !== \get_class($this)) {
197 $r = new \ReflectionMethod($this, __FUNCTION__);
198 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
199 @trigger_error(sprintf('The "%s()" method will have a third `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
206 return null !== $this->getNormalizer($data, $format, $context);
212 public function supportsDenormalization($data, $type, $format = null/*, array $context = array()*/)
214 if (\func_num_args() > 3) {
215 $context = \func_get_arg(3);
217 if (__CLASS__ !== \get_class($this)) {
218 $r = new \ReflectionMethod($this, __FUNCTION__);
219 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
220 @trigger_error(sprintf('The "%s()" method will have a fourth `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
227 return null !== $this->getDenormalizer($data, $type, $format, $context);
231 * Returns a matching normalizer.
233 * @param mixed $data Data to get the serializer for
234 * @param string $format Format name, present to give the option to normalizers to act differently based on formats
235 * @param array $context Options available to the normalizer
237 * @return NormalizerInterface|null
239 private function getNormalizer($data, $format, array $context)
241 foreach ($this->normalizers as $normalizer) {
242 if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format, $context)) {
249 * Returns a matching denormalizer.
251 * @param mixed $data Data to restore
252 * @param string $class The expected class to instantiate
253 * @param string $format Format name, present to give the option to normalizers to act differently based on formats
254 * @param array $context Options available to the denormalizer
256 * @return DenormalizerInterface|null
258 private function getDenormalizer($data, $class, $format, array $context)
260 foreach ($this->normalizers as $normalizer) {
261 if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format, $context)) {
270 final public function encode($data, $format, array $context = array())
272 return $this->encoder->encode($data, $format, $context);
278 final public function decode($data, $format, array $context = array())
280 return $this->decoder->decode($data, $format, $context);
286 public function supportsEncoding($format/*, array $context = array()*/)
288 if (\func_num_args() > 1) {
289 $context = \func_get_arg(1);
291 if (__CLASS__ !== \get_class($this)) {
292 $r = new \ReflectionMethod($this, __FUNCTION__);
293 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
294 @trigger_error(sprintf('The "%s()" method will have a second `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
301 return $this->encoder->supportsEncoding($format, $context);
307 public function supportsDecoding($format/*, array $context = array()*/)
309 if (\func_num_args() > 1) {
310 $context = \func_get_arg(1);
312 if (__CLASS__ !== \get_class($this)) {
313 $r = new \ReflectionMethod($this, __FUNCTION__);
314 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
315 @trigger_error(sprintf('The "%s()" method will have a second `$context = array()` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
322 return $this->decoder->supportsDecoding($format, $context);