2013-12-19 7 views
5

Ich mag ein einzelnes Objekteigenschaft auf Serialisierung und Deserialisierung mit JMSSerializer zu handhaben. Suposing wir diese Klasse haben:Format Eingabe- und Ausgabefelder mit JMSSerializer (Griff einzelne Eigenschaft)

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

Ich mag würde Instanzen Aufgabe zurückzukehren, um eine REST-API warf (mit FOSRestBundle). Das Problem ist, dass ich nicht den Rohwert des Attributs $status zurückgeben möchte, sondern den "Label" -Wert.

konfigurieren meine Serialisierung wie folgt aus:

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

Der JMS-Serializer hält den Rohwert, die 0 oder 1 ist, aber ich möchte schicken ‚offen‘ oder ‚über‘ in meinem serialisierte Objekt (mit getStatusLabel) . Und machen Sie den umgekehrten Job bei der Deserialisierung (mit getStatusFromLabel).

Ich dachte über eine virtual_properties, aber es funktioniert nur in Serilisation Richtung.

Ich habe versucht, mit einem benutzerdefinierten Handler wie folgt aussehen:

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

Aber es funktioniert natürlich nicht!

Wie kann ich meine benutzerdefinierten Getters sowohl in Serilisierung als auch in Deserialisierung aufrufen?

Antwort

10

Ich habe endlich die Antwort gefunden.

Zuerst habe ich ein Ereignis Subsciber wie diese erstellen hatte:

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

Was ich hier mache:

  • Vor Serialisierung, habe ich den Statuswert meines Aufgaben Objekt mit der Bezeichnung
  • Vor Deserialisierung, ändere ich den Wert des serialisierten Objekts aus dem Etikett auf den rohen ganzzahligen Wert

Für diese Lösung muss das Feld dem Serializer ausgesetzt werden (expose: true oder @Expose).

Dann erklärte ich den Abonnenten als Service in Symfony mit dem Tag jms_serializer.event_subscriber.

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

Und es funktioniert.

Es ist die beste Weise, die ich sowohl serialize und deserialize gefunden. Es ist auch möglich, Daten in den Ereignissen post_serialize und post_deserialize zu bearbeiten. Fügen Sie beispielsweise ein neues Feld für ein serialisiertes Objekt hinzu:

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

Vielen Dank für die Freigabe. @all Beachten Sie, dass der vollständig qualifizierte Name von 'class' ohne führenden Backslash (schwer zu debuggen) sein muss. – ownking