symfony/serializer usage with object collection

Symfony serializer lost collection object while denormalizing.

Example:

We have some entity with collection in the property (I’m using Doctrine ArrayCollection but it is reproduced for any Collection which implement PHP Iterator interface)

Let’s serialize and deserialize it with Symfony and JMS serializer and compare results:

Origin:
^ AppEntityEntity^ {#195
  -rewardItems: DoctrineCommonCollectionsArrayCollection^ {#179
    -elements: array:2 [
      0 => AppEntityItem^ {#224
        -type: 1
      }
      1 => AppEntityItem^ {#180
        -type: 2
      }
    ]
  }
}

Symfony deserialized:
^ AppEntityEntity^ {#192
  -rewardItems: array:2 [
    0 => AppEntityItem^ {#343
      -type: 1
    }
    1 => AppEntityItem^ {#325
      -type: 2
    }
  ]
}

Jms Deserialized
^ AppEntityEntity^ {#369
  -rewardItems: DoctrineCommonCollectionsArrayCollection^ {#357
    -elements: array:2 [
      0 => AppEntityItem^ {#374
        -type: 1
      }
      1 => AppEntityItem^ {#289
        -type: 2
      }
    ]
  }
}

Pay attention, that both original entity and JMS deserialized entity has internal ArrayCollection, while Symfony deserialized entity lost ArrayCollection and replace it with php array.

I found only one solution: add my own Symfony normalizer. But I see very poor developer experience here. For each Entity in my project I should provide boilerplate normalizer 🙁

Does symfony have simpler solution for Collections (f.e. like JMS) ?

Reproduce stand:

  1. Symfony Framework 5.2 wit common flex configuration
symfony new serializer-demo
composer.phar require serializer doctrine jmsserializer
  1. Very simple entities
class Entity
{
    /**
     * @var ArrayCollection<AppEntityItem>
     *
     * @Type("ArrayCollection<AppEntityItem>")
     *
     */
    private $rewardItems;

    public function __construct()
    {
        $this->rewardItems = new ArrayCollection();
    }

    public function getRewardItems(): ArrayCollection
    {
        return $this->rewardItems;
    }

    public function setRewardItems($rewardItems): void
    {
        $this->rewardItems = $rewardItems;
    }
}

class Item
{
    /**
     * @var int
     * @Type("int")
     */
    private $type;


    public function __construct(int $type)
    {
        $this->type = $type;
    }

    /**
     * @return int
     */
    public function getType(): int
    {
        return $this->type;
    }

    /**
     * @param int $type
     */
    public function setType(int $type): void
    {
        $this->type = $type;
    }

}

  1. Run in console command
        $a = new Entity();
        $a->getRewardItems()->add(new Item(1));
        $a->getRewardItems()->add(new Item(2));

        $output->writeln("Origin:");
        VarDumper::dump($a);

        $this->symfony($a, $output);
        $this->jms($a, $output);

// Symfony
        $serializer = $this->serializer; // injected by DI

        $serialized = $serializer->serialize($a, 'json');

        $d = $serializer->deserialize($serialized, Entity::class, 'json');
        $output->writeln("<warning>Symfony deserialized:</warning>");
        VarDumper::dump($d);

// JMS
        $serializer = SerializerBuilder::create()->build();

        $jsonContent = $serializer->serialize($a, 'json');

        $des = $serializer->deserialize($jsonContent, Entity::class, 'json');
        $output->writeln("Jms Deserialized");
        VarDumper::dump($des);```

Source: Symfony Questions

Was this helpful?

0 / 0

Leave a Reply 0

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