【问题标题】:symfony deserialize nested objectssymfony 反序列化嵌套对象
【发布时间】:2018-09-21 13:33:54
【问题描述】:

我已经使用 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;
    }
}

这很好用!

【问题讨论】:

  • 我通常将策略模式与 symfony 的序列化程序组件一起使用。它允许您准确定义规范化和非规范化对象/数组的外观。稍后我会发送一个示例。
  • 是的,一个例子可能很好,因为我不明白:-)

标签: serialization deserialization symfony-3.4


【解决方案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');

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

【讨论】:

    【解决方案2】:

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

    因此,您的想法是为规范化和非规范化创建单独的类,如下所示:

    ./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/Normalizer

    <?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');
    

    当然,在您的非规范化器中,您可以将数组转换为普通的旧 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

    尤其是顶部的图片,它是一个很好的记忆提示,告诉你应该以哪种方式使用每个类。

    希望这会有所帮助。

    【讨论】:

    • 如果您使用自动装配,则不需要将(反)规范化器定义为服务或标记它们,这应该是开箱即用的。只要你做过composer require serializer ;)
    • 谢谢爱德华。如果我理解得很好,您几乎可以自己完成所有工作,对吧? Symfony 序列化器几乎没用,不是吗?
    • 这取决于你,如果你想在没有自定义(反)规范化器的情况下进行序列化或反序列化,你可以直接使用序列化器服务$json = $this-&gt;serializer-&gt;serialize($object, 'json'); 但在你的问题中你想要一个自定义结构,你需要这样做自定义(反)规范化器。
    • 我找到了如何做到这一点。正如您在我的问题的编辑部分看到的,我已经测试了一个解决方案,该解决方案似乎不起作用,但最终运行良好。感谢您的帮助
    猜你喜欢
    • 2014-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 2019-11-15
    相关资源
    最近更新 更多