2013-12-19 79 views
5

我想通過JMSSerializer處理序列化和反序列化中的單個對象屬性。我們有這個類:使用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]; 
    } 
} 

我想返回任務的實例拋出一個REST API(使用FOSRestBundle)。問題是我不想返回$status屬性的原始值,而是返回「標籤」值。

配置我的序列是這樣的:

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

的JMS串行認爲原始值是0或1,但我想發送「等待處理」或「上方」在我的序列化的對象(使用getStatusLabel) 。並在反序列化中做相反的工作(使用getStatusFromLabel)。

我想要一個virtual_properties但它只適用於serilization方向。

我試圖用一個自定義的處理程序看起來像這樣:

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); 
    } 

但它顯然是行不通的!

我怎麼能在調整和反序列化的方向上調用我的自定義getters?

回答

10

我終於找到了答案。

首先,我不得不創建一個事件subsciber這樣的:

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); 
    } 
} 

我在做什麼在這裏:

  • 系列化之前,我把我的任務對象的狀態值與標籤
  • 在反序列化之前,我將序列化對象的值從標籤更改爲原始整數值

對於此解決方案,必須將字段公開(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())); 
} 
+0

感謝您的分享。 @all請考慮'class'的完全限定名稱必須沒有前導反斜槓(難以調試)。 – ownking