symfony反序列化嵌套对象

时间:2018-04-11 15:22:42

标签: serialization deserialization symfony-3.4

我已经使用Symfony序列化程序来序列化我的Recherche对象。 在Recherche对象中,我有子对象:CategorieLieu

当我反序列化我的Recherche对象时,所有子对象都在数组中转换。我希望他们再次成为对象。

这就是我序列化对象的方式:

$encoders = array(new JsonEncoder());
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('parent', 'enfants'));
$normalizer->setCircularReferenceHandler(function ($object) {
    return $object->getCode();
});
$normalizers = array($normalizer);
$serializer = new Serializer($normalizers, $encoders);
$rechercheJson= $serializer->serialize($recherche, 'json');

这就是我反序列化的方式:

$encoders = array(new JsonEncoder());
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('parent', 'enfants'));
$normalizer->setCircularReferenceHandler(function ($object) {
    return $object->getCode();
});
$normalizers = array($normalizer);
$serializer = new Serializer($normalizers, $encoders);
$recherche = $serializer->deserialize($recherche_json, Recherche::class, 'json');

我想也许与规范化程序有关,但我找不到任何可以帮助我完成文档的工作。

任何人都有想法提供帮助吗?

谢谢!

编辑: 看到这篇文章后:Denormalize nested structure in objects with symfony 2 serializer

我试过了:

$encoders = array(new JsonEncoder());
            $normalizer = new ObjectNormalizer(null, null, null, new SerializationPropertyTypeExtractor());
            $normalizer->setIgnoredAttributes(array('parent', 'enfants'));
            $normalizer->setCircularReferenceHandler(function ($object) {
                return $object->getCode();
            });
            $normalizers = array($normalizer,  new ArrayDenormalizer());
            $serializer = new Serializer($normalizers, $encoders);
            $recherche = $serializer->deserialize($recherche_json, Recherche::class, 'json');

SerializationPropertyTypeExtractor:

class SerializationPropertyTypeExtractor implements PropertyTypeExtractorInterface {
    /**
     * {@inheritdoc}
     */
    public function getTypes($class, $property, array $context = array())
    {
        if (!is_a($class, Recherche::class, true)) {
            return null;
        }

        if ('make' !== $property) {
            return null;
        }

        if ('lieu' === $property)
        {
            return [new Type(Type::BUILTIN_TYPE_OBJECT, true, LieuRecherche::class)];
        }
        if ('categorie' === $property)
        {
            return [new Type(Type::BUILTIN_TYPE_OBJECT, true, Categorie::class)];
        }

        return null;
    }
}

这很好用!

2 个答案:

答案 0 :(得分:1)

我遇到了类似的问题,并尝试使用自定义的PropertyTypeExtractor解决该问题。 由于我有很多带有嵌套对象的实体,因此当嵌套对象再次嵌套对象时,这也很麻烦。

我使用PhpDocExtractor和ReflectionExtractor找到了更好的解决方案,它们可以为您提取属性信息。

$encoder = [new JsonEncoder()];
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
$normalizer = [new ArrayDenormalizer(), new ObjectNormalizer(null, null, null, $extractor)];
$serializer = new Serializer($normalizer, $encoder);
$result = $serializer->deserialize($data,someEntity::class,'json');

这为我完成了所有工作。我希望这会对某人有所帮助。

答案 1 :(得分:0)

参考:我的评论。 我知道您正在以经典的方式使用序列化器,但我建议您应用推荐的策略模式,这样您就可以完全控制组件并且可以轻松扩展。

所以我的想法是你为标准化和非规范化创建单独的类,如下所示:

./ SRC /解归

<?php

declare(strict_types=1);

namespace App\Denormalizer;

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * Class ConfigDenormalizer
 * @package App\Denormalizer
 */
class ConfigDenormalizer implements DenormalizerInterface
{

    /**
     * @param mixed $data
     * @param string $class
     * @param null $format
     * @param array $context
     * @return array
     */
    public function denormalize($data = [], $class, $format = null, array $context = array())
    {
        $result = [];
        foreach($data as $key => $config) {
            $result[ $config['attribute'] ] = $config['valorem'];
        }
        return $result;
    }

    /**
     * @param mixed $data
     * @param string $type
     * @param null $format
     * @return bool
     */
    public function supportsDenormalization($data, $type, $format = null)
    {
        return $type === self::class;
    }
}

./ SRC /正规化

<?php

declare(strict_types=1);

namespace App\Normalizer;

use Symfony\Component\Form\Form;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * Class WidgetConfigNormalizer
 * @package App\Normalizer
 */
class WidgetAttributeNormalizer implements NormalizerInterface
{
    /**
     * @param object $form
     * @param null $format
     * @param array $context
     * @return array
     */
    public function normalize($form, $format = null, array $context = array()): array
    {
        $result = [];

        foreach($form->getData() as $key => $value) {
            $result[] = [
                'attribute' => (string) $key,
                'valorem' => (string) $value,
            ];
        }

        return $result;
    }

    /**
     * @param mixed $form
     * @param null $format
     * @return bool
     */
    public function supportsNormalization($form, $format = null)
    {
        if($form instanceof Form) {
            return $form->getName() === 'widgetAttribute';
        }
    }

}

你可以这样称呼它:

//denormalize
$this->denormalizer->denormalize(
    json_decode($response->getContent(), true),
    ConfigDenormalizer::class
);

//normalize
$form = $this->formFactory->create(myConfigFormType::class);
$form->submit($data);
$this->normalizer->normalize($form);

或者如果你想使用序列化器(注意我们不需要json_decode):

//deserialize
$array = $this->serializer->deserialize(
    $response->getContent(),
    ConfigDenormalizer::class,
    'json'
);

//serialize
$json = $this->serialize->serialize($form, 'json');

当然在你的denormalizer中你可以将你的数组转换成一个普通的旧php对象(实体)。或只输出一个数组,选择权在你手中。

这样你所做的就是将SerializerInterface注入你的控制器:

use Symfony\Component\Serializer\SerializerInterface;

class EmbedController extends Controller
{
    /**
    * @var SerializerInterface
    */
    private $serializer;

    public function __construct(SerializerInterface $serializer)
    {
        $this->serializer = $serializer;
    }

}

这种方法使得单元测试也变得更加容易,因为所有东西都是分离的,因此可以进行模拟。 :)

它还值得检查文档: https://symfony.com/doc/current/components/serializer.html

特别是顶部的图像,它是一个很棒的记忆提示,关于你应该使用每个类的方式。

希望这有帮助。