简体   繁体   中英

Callback on serializer Symfony

I'm running Symfony 2.7 and I'm trying output an object (Doctrine entity) as JSON.

When I'm normalizing the object I want to convert some of it's values. To do this I found the "setCallbacks" method in the documentation but I'm kinda stumped on how to apply it to my case.

Is there any way to call the "setCallbacks" method on the normalizer that is set when calling Symfonys serializer service?

Here is a short example of what I'm trying to achieve:

//ExampleController.php

public function getJSONOrderByIdAction($id) {
    $serializer = $this->get('serializer');
    $normalizer = $serializer->getNormalizer(); // <- This is what I'm unable to do

    $dateTimeToString = function ($dateTime) {
        return $dateTime instanceof \DateTime ? $dateTime->format(\DateTime::ISO8601) : '';
    };

    $normalizer->setCallbacks(['time' => $dateTimeToString]);


    $order = $this->getDoctrine()->find("AppBundle:Order", $id);

    return new JsonResponse(["order" => $serializer->normalize($order, null, ["groups" => ["public"]])]);
}

I'm aware that most people have switched to the JMS serializer. It just seems as if the built in serializer should be able to handle what I'm trying to achieve.

The default Serializer service is created during dependency injection phase, and the Serializer interface do not allow editing of (full) retrieval of normalizers.

I think you have (at least) three choice here:

  1. add your custom normalizer to the default Serializer service
  2. add NormalizableInterface to your entities
  3. create a new Serializer service (or a local object as suggested by the docs) as you were trying to do.

I think in your scenario, case 1 is preferred (since 2 becomes boring pretty fast).

I would do something like this; first create a custom Normalizer

<?php
namespace AppBundle; 

class DateTimeNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
    /**
     * {@inheritdoc}
     */
    public function normalize($object, $format = null, array $context = array())
    {
        return $object->format(\DateTime::ISO8601);
    }

    /**
     * {@inheritdoc}
     */
    public function denormalize($data, $class, $format = null, array $context = array())
    {
        return new $class($data);
    }

    /**
     * Checks if the given class is a DateTime.
     *
     * @param mixed  $data   Data to normalize.
     * @param string $format The format being (de-)serialized from or into.
     *
     * @return bool
     */
    public function supportsNormalization($data, $format = null)
    {
        return $data instanceof \DateTime;
    }

    /**
     * Checks if the given class is a DateTime.
     *
     * @param mixed  $data   Data to denormalize from.
     * @param string $type   The class to which the data should be denormalized.
     * @param string $format The format being deserialized from.
     *
     * @return bool
     */
    public function supportsDenormalization($data, $type, $format = null)
    {
        $class = new \ReflectionClass($type);

        return $class->isSubclassOf('\DateTime');
    }
}

Then register it to your services:

# app/config/services.yml
services:
    datetime_normalizer:
        class: AppBundle\DateTimeNormalizer
        tags:
            - { name: serializer.normalizer }

My own solution

Following the advice from giosh94mhz I tried switching to JMS Serializer but ended up going back to Symfonys serializer.

JMS Serializer presented it's own issues and while searching for answers for those I stumbled upon a blog post by Thomas Jarrand that did an excellent job explaining how to make and implement your own normalizers in Symfony.

In my opinion, you seem to be trying to over-complicate things. Here's the approach I've taken when I needed to serialize my entities as JSON:

PHP 2.5 and above allows you to implement the jsonSerialize method on your objects and just call json_encode directly on your object.

If you are still using PHP 2.4, you just need to manually call jsonSerialize() on your objects.

For example:

/**
 * @ORM\Entity
 */
class MyEntity {
    ...
    public function jsonSerialize() {
        $data = array("foo" => $this->bar());
        // add other data here ...
        return $data
    }
}

And then in calling code:

// for PHP 2.5 and up:
$normalized = json_encode($myEntityInstance);

// for PHP 2.4 and below
$normalized = json_encode($myEntityInstance->jsonSerialize());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM