Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
906 views
in Technique[技术] by (71.8m points)

php - 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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: AppBundleDateTimeNormalizer
        tags:
            - { name: serializer.normalizer }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...