Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I deserialize an array of objects in Symfony Serializer?

Is is possible in Symfony Serializer to deserialize an array of objects in a property? I have a Boss class with the $Npc = [] property that needs to hold a array of Npc objects. I did see some examples in the documentation, but they do not state this feature. I have a json string with an array of NPC's For example:

class Boss {

    private $Npc = [];    

    /**
    * @return Npc[]
    */
    public function getNpcs(): array
    {
        return $this->npcs;
    }
}

I am using php7.1 and symfony/serializer version ^3.3.

Edit: I already tried PhpDocExtractor, but it would not let me install it. :(

Edit: This is a possible JSON value:

{
    "bossname": "Epic boss!",
    "npcs": [{
        "id": 24723,
        "name": "Selin Fireheart",
        "urlSlug": "selin-fireheart",
        "creatureDisplayId": 22642
    }]
}
like image 573
Mike Rovers Avatar asked Nov 13 '17 20:11

Mike Rovers


3 Answers

The following (full) example is working for me with Symfony 5.3 components. It combines some of the above and adds some more necessary parts:

The Serializer needs to include ArrayDenormalizer as well as ObjectNormalizer. The ObjectNormalizer needs proper configuration. The full creation can look like this:

return new Serializer(
    [
        new ArrayDenormalizer(),
        new DateTimeNormalizer(),
        new ObjectNormalizer(
            null,
            null,
            null,
            new ReflectionExtractor()
        ),
    ],
    [
        new JsonEncoder(),
    ]
);

It seems like order is important regarding the normalizer / denormalizer, probably due to priority. Placing DateTimeNormalizer after ObjectNormalizer won't work.

The target class needs to provide three methods in order to allow mapping of an array of other classes:

class Place
{
    /**
     * @var OpeningHour[]
     */
    protected $openingHours = [];

    /**
     * @return OpeningHour[]
     */
    public function getOpeningHoursSpecification(): array
    {
        return $this->openingHours;
    }

    public function addOpeningHoursSpecification(OpeningHour $openingHour): void
    {
        $this->openingHours[] = $openingHour;
    }

    public function removeOpeningHoursSpecification(OpeningHour $openingHour): void
    {
    }
}

All three methods need to exist. The first in order to allow Serializer to fetch existing values and allow comparison. The other two need to exist in order to adapt existing value to match expected one by adding missing and removing no longer existing entities.

The above does not provide implementation for removal as this is taken from a one way project.

like image 193
Daniel Avatar answered Nov 16 '22 20:11

Daniel


I found a way to do this :). I installed the Symfony PropertyAccess package through Composer. With this package, you can add adders, removers and hassers. This way Symfony Serializer will automaticly fill the array with the correct objects. Example:

private $npcs = [];

public function addNpc(Npc $npc): void
{
    $this->npcs[] = $npc;
}

public function hasNpcs(): bool
{
    return count($this->npcs) > 0
}

etc.

This way you can use the ObjectNormalizer with:

$normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor());

Edit: At least since v3.4 you have to create a remover method as well. Otherwise it just won't work (no error or warning).

like image 10
Mike Rovers Avatar answered Nov 16 '22 21:11

Mike Rovers


I have struggled with this many hours without getting a result. Every time i added an adder function, the objectnormalizer wanted to invoke this function but got an error something like "The field xyz should be of type xyz[], array given".

This is cause i forgot to add the ArrayDenormalizer to the normalizer pool of the serializer. After adding this, everything worked fine.

Hope this is helpful for somebody.

like image 6
Jim Panse Avatar answered Nov 16 '22 20:11

Jim Panse