Symfony serializer : discriminate on structure (or data type)

I run into an issue when I try to deserialize some JSON with an interface.
JSON data structure is changing and there’s no scalar data to rely on. Note that I’m not able to modify the JSON and that I am not able to ask for one structure or another.
As I did not find any way to handle mapping on data type, I thought I would inject a virtual value during normalize process.

Unfortunately it does not seem to be possible, the discriminator seems to be triggered before data is added.

Am I missing something ?

A sample of code to illustrate my problem

<?php

namespace Test;

use SymfonyComponentSerializerAnnotationDiscriminatorMap;

/**
 * @DiscriminatorMap(typeProperty="type", mapping={
 *    "string"="TestBar1",
 *    "array"="TestBar2"
 * })
 */
interface Foo
{

}
<?php

namespace Test;

class Bar1 implements Foo
{

    /**
     * @var string
     */
    public $type;

    /**
     * @var string
     */
    public $foo;

}
<?php

namespace Test;

class Bar2 implements Foo
{

    /**
     * @var string
     */
    public $type;

    /**
     * @var array
     */
    public $foo;

}
<?php

namespace Test;

use SymfonyComponentSerializerNormalizerObjectNormalizer;

class FooNormalizer extends ObjectNormalizer
{
    public function normalize($data, $format = null, array $context = [])
    {
        $data         = $this->normalizer->normalize($data, $format, $context);
        $data['type'] = is_string($data['foo'])
            ? 'string'
            : 'array';

        return $data;
    }
}

So when I launch my test:

<?php

use DoctrineCommonAnnotationsAnnotationReader;
use SymfonyComponentPropertyInfoExtractorPhpDocExtractor;
use SymfonyComponentSerializerEncoderJsonEncoder;
use SymfonyComponentSerializerMappingClassDiscriminatorFromClassMetadata;
use SymfonyComponentSerializerMappingFactoryClassMetadataFactory;
use SymfonyComponentSerializerMappingLoaderAnnotationLoader;
use SymfonyComponentSerializerNameConverterMetadataAwareNameConverter;
use SymfonyComponentSerializerNormalizerAbstractObjectNormalizer;
use SymfonyComponentSerializerSerializer;

use TestFoo;
use TestFooNormalizer;

try {

    $data = [];

    $data[] = <<<JSON
{
    "foo": {
        "bar": "hey"
    }
}
JSON;

    $data[] = <<<JSON
{
    "foo": {
        "bar": ["hey", "ho"]
    }
}
JSON;

    $class_metadata_factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())),

    $serializer = new Serializer(
            [
                new FooNormalizer(
                    class_metadata_factory,
                    new MetadataAwareNameConverter(
                        $class_metadata_factory
                    ),
                    null,
                    new PhpDocExtractor(),
                    new ClassDiscriminatorFromClassMetadata($class_metadata_factory)
                )
            ],
            ['json' => new JsonEncoder()]
        );


    foreach ($data as $d) {

        $rs = $serializer->deserialize($d, Foo::class, 'json');

    }


} catch (Exception $e) {

    echo $e->getMessage();

}

I get the exception:

Type property "type" not found for the abstract object "Foo"

Did anybody already had this kind of issue and found a way to handle it ?

Source: Symfony Questions

Was this helpful?

0 / 0

Leave a Reply 0

Your email address will not be published. Required fields are marked *