<?php
namespace JMS\Serializer;
use JMS\Serializer\Construction\ObjectConstructorInterface;
use JMS\Serializer\ContextFactory\DefaultDeserializationContextFactory;
use JMS\Serializer\ContextFactory\DefaultSerializationContextFactory;
use JMS\Serializer\ContextFactory\DeserializationContextFactoryInterface;
use JMS\Serializer\ContextFactory\SerializationContextFactoryInterface;
use JMS\Serializer\EventDispatcher\EventDispatcherInterface;
use JMS\Serializer\Exception\RuntimeException;
use JMS\Serializer\Exception\UnsupportedFormatException;
use JMS\Serializer\Expression\ExpressionEvaluatorInterface;
use JMS\Serializer\Handler\HandlerRegistryInterface;
use Metadata\MetadataFactoryInterface;
use PhpCollection\MapInterface;
/**
* Serializer Implementation.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class Serializer implements SerializerInterface, ArrayTransformerInterface
{
private $factory;
private $handlerRegistry;
private $objectConstructor;
private $dispatcher;
private $typeParser;
/** @var \PhpCollection\MapInterface */
private $serializationVisitors;
/** @var \PhpCollection\MapInterface */
private $deserializationVisitors;
private $navigator;
/**
* @var SerializationContextFactoryInterface
*/
private $serializationContextFactory;
/**
* @var DeserializationContextFactoryInterface
*/
private $deserializationContextFactory;
/**
* Constructor.
*
* @param \Metadata\MetadataFactoryInterface $factory
* @param Handler\HandlerRegistryInterface $handlerRegistry
* @param Construction\ObjectConstructorInterface $objectConstructor
* @param \PhpCollection\MapInterface $serializationVisitors of VisitorInterface
* @param \PhpCollection\MapInterface $deserializationVisitors of VisitorInterface
* @param EventDispatcher\EventDispatcherInterface $dispatcher
* @param TypeParser $typeParser
* @param ExpressionEvaluatorInterface|null $expressionEvaluator
*/
public function __construct(
MetadataFactoryInterface $factory,
HandlerRegistryInterface $handlerRegistry,
ObjectConstructorInterface $objectConstructor,
MapInterface $serializationVisitors,
MapInterface $deserializationVisitors,
EventDispatcherInterface $dispatcher = null,
TypeParser $typeParser = null,
ExpressionEvaluatorInterface $expressionEvaluator = null
)
{
$this->factory = $factory;
$this->handlerRegistry = $handlerRegistry;
$this->objectConstructor = $objectConstructor;
$this->dispatcher = $dispatcher;
$this->typeParser = $typeParser ?: new TypeParser();
$this->serializationVisitors = $serializationVisitors;
$this->deserializationVisitors = $deserializationVisitors;
$this->navigator = new GraphNavigator($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->dispatcher, $expressionEvaluator);
$this->serializationContextFactory = new DefaultSerializationContextFactory();
$this->deserializationContextFactory = new DefaultDeserializationContextFactory();
}
public function serialize($data, $format, SerializationContext $context = null)
{
if (null === $context) {
$context = $this->serializationContextFactory->createSerializationContext();
}
return $this->serializationVisitors->get($format)
->map(function (VisitorInterface $visitor) use ($context, $data, $format) {
$type = $context->getInitialType() !== null ? $this->typeParser->parse($context->getInitialType()) : null;
$this->visit($visitor, $context, $visitor->prepare($data), $format, $type);
return $visitor->getResult();
})
->getOrThrow(new UnsupportedFormatException(sprintf('The format "%s" is not supported for serialization.', $format)));
}
public function deserialize($data, $type, $format, DeserializationContext $context = null)
{
if (null === $context) {
$context = $this->deserializationContextFactory->createDeserializationContext();
}
return $this->deserializationVisitors->get($format)
->map(function (VisitorInterface $visitor) use ($context, $data, $format, $type) {
$preparedData = $visitor->prepare($data);
$navigatorResult = $this->visit($visitor, $context, $preparedData, $format, $this->typeParser->parse($type));
return $this->handleDeserializeResult($visitor->getResult(), $navigatorResult);
})
->getOrThrow(new UnsupportedFormatException(sprintf('The format "%s" is not supported for deserialization.', $format)));
}
/**
* {@InheritDoc}
*/
public function toArray($data, SerializationContext $context = null)
{
if (null === $context) {
$context = $this->serializationContextFactory->createSerializationContext();
}
return $this->serializationVisitors->get('json')
->map(function (JsonSerializationVisitor $visitor) use ($context, $data) {
$type = $context->getInitialType() !== null ? $this->typeParser->parse($context->getInitialType()) : null;
$this->visit($visitor, $context, $data, 'json', $type);
$result = $this->convertArrayObjects($visitor->getRoot());
if (!\is_array($result)) {
throw new RuntimeException(sprintf(
'The input data of type "%s" did not convert to an array, but got a result of type "%s".',
\is_object($data) ? \get_class($data) : \gettype($data),
\is_object($result) ? \get_class($result) : \gettype($result)
));
}
return $result;
})
->get();
}
/**
* {@InheritDoc}
*/
public function fromArray(array $data, $type, DeserializationContext $context = null)
{
if (null === $context) {
$context = $this->deserializationContextFactory->createDeserializationContext();
}
return $this->deserializationVisitors->get('json')
->map(function (JsonDeserializationVisitor $visitor) use ($data, $type, $context) {
$navigatorResult = $this->visit($visitor, $context, $data, 'json', $this->typeParser->parse($type));
return $this->handleDeserializeResult($visitor->getResult(), $navigatorResult);
})
->get();
}
private function visit(VisitorInterface $visitor, Context $context, $data, $format, array $type = null)
{
$context->initialize(
$format,
$visitor,
$this->navigator,
$this->factory
);
$visitor->setNavigator($this->navigator);
return $this->navigator->accept($data, $type, $context);
}
private function handleDeserializeResult($visitorResult, $navigatorResult)
{
// This is a special case if the root is handled by a callback on the object itself.
if (null === $visitorResult && null !== $navigatorResult) {
return $navigatorResult;
}
return $visitorResult;
}
private function convertArrayObjects($data)
{
if ($data instanceof \ArrayObject || $data instanceof \stdClass) {
$data = (array)$data;
}
if (\is_array($data)) {
foreach ($data as $k => $v) {
$data[$k] = $this->convertArrayObjects($v);
}
}
return $data;
}
/**
* @return MetadataFactoryInterface
*/
public function getMetadataFactory()
{
return $this->factory;
}
/**
* @param SerializationContextFactoryInterface $serializationContextFactory
*
* @return self
*/
public function setSerializationContextFactory(SerializationContextFactoryInterface $serializationContextFactory)
{
$this->serializationContextFactory = $serializationContextFactory;
return $this;
}
/**
* @param DeserializationContextFactoryInterface $deserializationContextFactory
*
* @return self
*/
public function setDeserializationContextFactory(DeserializationContextFactoryInterface $deserializationContextFactory)
{
$this->deserializationContextFactory = $deserializationContextFactory;
return $this;
}
}