phpsymfonyserializationsymfony4

Serialising with annotation groups


Symfony v4.2

I'm trying to use symfony's normalizer and serialiser to convert my entities into JSON.

I followed the Groups annotation pattern, because it sounds like the most intuitive and future proof way to do it.

According to the docs, we have to manually instantiate the ClassMetadataFactory in order to load the AnnotationLoader. I'm still using the ObjectNormalizer which worked fine before using annotations.

Here's my serialize method:

/**
 * @param bool $event Serialize all event fields (except references)
 * @param bool $meta Serialize referenced meta instance
 * @param bool $targets Serialize referenced target instances
 * @param bool $users Serialize referenced user instances
 * @param bool $videos Serialize referenced video instances
 * @param bool $photos Serialize referenced photo instances
 * @return string
 */
public function serialize($event = true, $meta = false, $targets = false, $users = false, $videos = false, $photos = false) {
    $serializingGroups = [];
    if ($event) {
        $serializingGroups[] = 'event';
    }
    if ($meta) {
        $serializingGroups[] = 'meta';
    }
    if ($targets) {
        $serializingGroups[] = "targets";
    }
    if ($users) {
        $serializingGroups[] = 'users';
    }
    if ($videos) {
        $serializingGroups[] = 'videos';
    }
    if ($photos) {
        $serializingGroups[] = 'photos';
    }

    $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
    $normalizer = new ObjectNormalizer($classMetadataFactory);
    $serializer = new Serializer([$normalizer], [new JsonEncoder()]);

    $data = $serializer->normalize($this, null, ['groups' => $serializingGroups]);
    return $serializer->serialize($data, 'json');
}

These groups are there to speed up serialization if I don't need associations te be serialized.

Now that I use group annotations, the json looks like this

{
    "id":2,
    "title":"test",
    "description":"<p>&nbsp;test<\/p>",
    "subscriptionPrice":"0.00",
    "subscriptionUntil":[], // DATE  - WHY EMPTY ARRAY?
    "start":[], // DATE  - WHY EMPTY ARRAY?
    "end":[], // DATE  - WHY EMPTY ARRAY?
    "allDay":false,
    "creationDate":[], // DATE  - WHY EMPTY ARRAY?
    "editDate":[], // DATE  - WHY EMPTY ARRAY?
    "editable":true,
    "startEditable":true,
    "durationEditable":true,
    "overlap":false,
    "color":"#9EABAB",
    "backgroundColor":"#9eabab",
    "borderColor":"#9eabab",
    "textColor":"#000000",
    "isRecurring":true,
    "meta":[], // ASSOCIATED ENTITY  - WHY EMPTY ARRAY?
    "targets":[[]] // ASSOCIATED COLLECTION OF ENTITIES  - WHY EMPTY ARRAY?
}

Why does it work when I don't use annotations? Why are dates and other entities empty arrays when I do use annotations?


Solution

  • Eureca!

    Here is a code snippet from my serialize method:

    $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
    $normalizer = new ObjectNormalizer($classMetadataFactory);
    $serializer = new Serializer([
        // Add a Datetimenormalizer as the first normalizer in the list
        new DatetimeNormalizer(),
        $normalizer
    ], [new JsonEncoder()]);
    
    return $serializer->serialize($this, 'json', [
        // $serializer->addCircularReferenceHandler() IS DEPRECATED
        // define the handler like this:
        ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($event) {
            return '/event/'.$event->id;
        }
        ,ObjectNormalizer::GROUPS => $serializingGroups
    ]);
    

    First of all, the reason why meta and targets weren't serializing was because I had the wrong import in my Meta and Target entities. Make sure it is: use Symfony\Component\Serializer\Annotation\Groups;... so... yeah... moving on.

    The dates did serialize when I commented out //,ObjectNormalizer::GROUPS => $serializingGroups. So why don't they serialize when groups aren't commented out? My guess is that the DateTime class doesn't have the Groups annotations, so the normalizer was never able to get any of the DateTime attributes

    This is solved by adding a DatetimeNormalizer as the first normalizer in the array: $serializer = new Serializer([new DatetimeNormalizer(),$normalizer])

    This is how my entities serialize correctly :D