使用JMSSerializer格式化输入和输出字段(处理单个属性)

时间:2013-12-19 17:01:34

标签: jmsserializerbundle

我想使用JMSSerializer处理序列化和反序列化的单个对象属性。我们有这个课程:

class Task {

    const STATUS_PENDING = 0;
    const STATUS_OVER    = 1;

    protected $status;

    /* getter and setter */

    public function getStatusLabel()
    {
        return ['pending', 'over'][$this->getStatus()];
    }

    public static function getStatusFromLabel($label)
    {
        return [
            'pending' => self::STATUS_PENDING,
            'over'    => self::STATUS_OVER
        ][$label];
    }
}

我想返回Task的实例抛出一个REST API(使用FOSRestBundle)。问题是我不想返回$status属性的原始值,而是返回“label”值。

像这样配置序列化:

Task:
    exclusion_policy: ALL
    properties:
        status:
            expose: true
            type: string

JMS Serializer认为原始值为0或1,但我想在序列化对象中发送'pending'或'over'(使用getStatusLabel)。并且在反序列化方面做了相反的工作(使用getStatusFromLabel)。

我对virtual_properties感到满意,但它只能在血清化方向上起作用。

我尝试使用这样的自定义处理程序:

class TaskHandler implements SubscribingHandlerInterface
{
    public static function getSubscribingMethods()
    {
        return [
            [
                'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                'format' => 'json',
                'type' => 'Task',
                'method' => 'serializeToArray',
            ]
        ];
    }

    public function serializeToArray(JsonSerializationVisitor $visitor, Task $task, array $type, Context $context)
    {
        $task->setStatus($task->getStatusLabel());
        return $visitor->getNavigator()->accept($task, $type, $context);
    }

但它显然不起作用!

如何在血清化和反序列化方向上调用我的自定义getter?

1 个答案:

答案 0 :(得分:10)

我终于找到了答案。

首先,我必须创建一个这样的事件子网:

use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\Events;
use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;

class TaskSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            [
                'event' => Events::PRE_SERIALIZE,
                'format' => 'json',
                'class' => 'Task', // fully qualified name here
                'method' => 'onPreSerializeTaskJson',
            ],
            [
                'event' => Events::PRE_DESERIALIZE,
                'format' => 'json',
                'class' => 'Task',
                'method' => 'onPreDeserializeTaskJson',
            ]
        ];
    }

    public function onPreSerializeTaskJson(PreSerializeEvent $event)
    {
        /** @var Task $task */
        $task = $event->getObject();

        $task->setStatus($task->getStatusLabel());
    }

    public function onPreDeserializeTaskJson(PreDeserializeEvent $event)
    {
        $data = $event->getData();

        $data['status'] = Task::getStatusFromLabel($data['status']);

        $event->setData($data);
    }
}

我在这做什么:

  • 在序列化之前,我使用标签
  • 设置Task对象的状态值
  • 在反序列化之前,我将序列化对象的值从标签更改为原始整数值

对于此解决方案,必须将字段(expose: true@Expose)公开给序列化程序。

然后我将订阅者声明为Symfony中的服务,标记为jms_serializer.event_subscriber

serializer.subscriber.task:
    class: %serializer.subscriber.task.class% # TaskSubscriber class path
    tags:
        - { name: jms_serializer.event_subscriber }

它有效。

这是我发现序列化和反序列化的最佳方式。也可以在post_serialize和post_deserialize事件上操作数据。例如,在序列化对象上添加新字段:

use JMS\Serializer\EventDispatcher\ObjectEvent;

public function onPostSerializeTaskJson(ObjectEvent $event)
{
    /** @var Task $task */
    $task = $event->getObject();

    $event->getVisitor()->addData('nb_related', count($task->getRelatedTasks()));
}