Add extra fields using JMS Serializer bundle

后端 未结 6 1377
说谎
说谎 2020-12-13 04:03

I\'ve an entity I usually serialize using the JMS Serializer bundle. I have to add to the serialization some fields that doesn\'t reside in the entity itself but are gathere

相关标签:
6条回答
  • 2020-12-13 04:32

    The accepted answer only works when the visitor is derived from \JMS\Serializer\GenericSerializationVisitor. This means it will work for JSON, but fail for XML.

    Here's an example method which will cope with XML. It looks at the interfaces the visitor object supports and acts appropriately. It shows how you might add a link element to both JSON and XML serialized objects...

    public function onPostSerialize(ObjectEvent $event)
    {
        //obtain some data we want to add
        $link=array(
            'rel'=>'self',
            'href'=>'http://example.org/thing/1',
            'type'=>'application/thing+xml'
        );
    
        //see what our visitor supports...
        $visitor= $event->getVisitor();
        if ($visitor instanceof \JMS\Serializer\XmlSerializationVisitor)
        {
            //do XML things
            $doc=$visitor->getDocument();
    
            $element = $doc->createElement('link');
            foreach($link as $name => $value) {
                $element->setAttribute($name, $value);
            }
            $doc->documentElement->appendChild($element);
        } elseif ($visitor instanceof \JMS\Serializer\GenericSerializationVisitor)
        {
            $visitor->addData('link', $link);
        }
    
    
    }
    
    0 讨论(0)
  • 2020-12-13 04:34

    I am surprised why nobody have suggested a much more easier way. You need just to use @VirtualProperty:

    <?php
    
    // ...
    /**
     * @JMS\VirtualProperty
     * @JMS\SerializedName("someField")
     */
    public function getSomeField()
    {
        return $this->getTitle() . $this->getPromo();
    }
    
    0 讨论(0)
  • 2020-12-13 04:35

    addData is deprecated so since 2.0.0, so we need to do it like this:

    use JMS\Serializer\EventDispatcher\ObjectEvent;
    
    class MySerializerHandler {
    
        public function onPostSerialize(ObjectEvent $event)
        {
            /** @var MySpecialObjectType $object */
            $myObject = $event->getObject();
    
            $key = 'customDataKey';
            $value = 'myvalue';
    
            $event->getVisitor()->visitProperty(
                new StaticPropertyMetadata('', $key, $value),
                $value
            );
        }
    }
    

    services.yaml

    services:
        MySerializerHandler:
            tags:
              - { name: jms_serializer.event_listener, class: 'MySpecialObjectType', event: serializer.post_serialize, method: 'onPostSerialize' }
    

    https://github.com/schmittjoh/serializer/blob/c9c82c841b8ebe682ca44972d64fded215f72974/UPGRADING.md#from-1130-to-200

    0 讨论(0)
  • 2020-12-13 04:37

    What about this: http://jmsyst.com/libs/serializer/master/handlers

    In summary, you define a class that receives an object and returns text or an array (that will be converted to json).

    You have class "IndexedStuff" that contains a weird calculated field that for some reason should be calculated at serialization time.

    <?php
    
    namespace Project/Model;
    
    class IndexedStuff
    {
       public $name;
       public $value;
       public $rawData;
    }
    

    Now create the handler

    <?php
    
    namespace Project/Serializer;
    
    use JMS\Serializer\Handler\SubscribingHandlerInterface;
    use JMS\Serializer\GraphNavigator;
    use JMS\Serializer\JsonSerializationVisitor;
    use JMS\Serializer\Context;
    
    class MyHandler implements SubscribingHandlerInterface
    {
        public function setEntityManager(Registry $registry) {
             // Inject registry instead of entity manager to avoid circular dependency
             $this->em = $registry->getEntityManager();
        }
        public static function getSubscribingMethods()
        {
            return array(
                array(
                    'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                    'format' => 'json',
                    'type' => 'Project/Model/IndexedStuff',
                    'method' => 'serializeIndexedStuffToJson',
                ),
            );
        }
    
        public function serializeIndexedStuffToJson(JsonSerializationVisitor $visitor, Project/Model/IndexedStuff $stuff, array $type, Context $context)
        {
            // Build your object here and return it
            $score = $this->em->find("ProjectBundle:Calculator", $stuff->value)
            return array("score" => $score->getIndexScore(), "name"=> $score->name
        }
    }
    

    Finally register the service

    services:
      project.serializer.stuff:
          class: Project\Serializer\MyHandler
          calls:
            - [setEntityManager, ["@doctrine"]]
    

    Now everywhere you want to serialize an object of type "IndexedStuff" you will get a json like this

    {"name": "myName", "score" => 0.3432}

    By this way you can fully customize how your entity is serialized

    0 讨论(0)
  • 2020-12-13 04:45

    To further answer the original question. Here is how you limit added data for some serialized groups (in this example some_data is only added when we are not using the list group:

    public function onPostSerializeSomeEntityJson(ObjectEvent $event) {
    
        $entity = $event->getObject();
    
        if (!in_array('list', (array)$event->getContext()->attributes->get('groups'))) {
    
            $event->getVisitor()->addData('user_access', array(
                'some_data' => 'some_value'
            ));
        }
    }
    

    (array)$event->getContext()->attributes->get('groups') contains an array of the used serialized groups.

    0 讨论(0)
  • 2020-12-13 04:54

    I've found the solution by myself,

    to add a custom field after the serialization has been done we've to create a listener class like this:

    <?php
    
    namespace Acme\DemoBundle\Listener;
    
    use JMS\DiExtraBundle\Annotation\Service;
    use JMS\DiExtraBundle\Annotation\Tag;
    use JMS\DiExtraBundle\Annotation\Inject;
    use JMS\DiExtraBundle\Annotation\InjectParams;
    use Symfony\Component\HttpKernel\Event\PostResponseEvent;
    use Acme\DemoBundle\Entity\Team;
    use JMS\Serializer\Handler\SubscribingHandlerInterface;
    use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
    use JMS\Serializer\EventDispatcher\PreSerializeEvent;
    use JMS\Serializer\EventDispatcher\ObjectEvent;
    use JMS\Serializer\GraphNavigator;
    use JMS\Serializer\JsonSerializationVisitor;
    
    /**
     * Add data after serialization
     *
     * @Service("acme.listener.serializationlistener")
     * @Tag("jms_serializer.event_subscriber")
     */
    class SerializationListener implements EventSubscriberInterface
    {
    
        /**
         * @inheritdoc
         */
        static public function getSubscribedEvents()
        {
            return array(
                array('event' => 'serializer.post_serialize', 'class' => 'Acme\DemoBundle\Entity\Team', 'method' => 'onPostSerialize'),
            );
        }
    
        public function onPostSerialize(ObjectEvent $event)
        {
            $event->getVisitor()->addData('someKey','someValue');
        }
    }
    

    That way you can add data to the serialized element.

    Instead, if you want to edit an object just before serialization use the pre_serialize event, be aware that you need to already have a variable (and the correct serialization groups) if you want to use pre_serialize for adding a value.

    0 讨论(0)
提交回复
热议问题