Search code examples
phpserializationdoctrine-ormjms-serializer

JMS Deserialize ArrayCollection in Object


I'm trying to use JMS serializer in my application (not Symfony) and would like to deserialize a JSON object to the Doctrine Entity.
The plain properties are getting properly deserialized, but I can't get the ArrayCollections to work.

This is an excerpt of my product JSON:

{
  "id": 2,
  "name": "Shirt blue",
  "attributeValues": [
    {
      "id": 4,
      "title": "S",
      "attributeId": 2
    },
    {
      "id": 7,
      "title": "Eterna",
      "attributeId": 3
    }
  ]
}

This is my Product entity:

<?php

namespace Vendor\App\Common\Entities;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 * @ORM\Table(name="product")
 * @JMS\ExclusionPolicy("all")
 */
class Product extends AbstractEntity {

    /**
     * @var int $id
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @JMS\Groups({"search"})
     * @JMS\Expose
     * @JMS\Type("integer")
     */
    protected $id;

    /**
     * @var string $name
     * @ORM\Column(name="name", type="string", nullable=false)
     * @JMS\Expose
     * @JMS\Groups({"search"})
     * @JMS\Type("string")
     */
    protected $name;

    /**
     * @var ArrayCollection $attributeValues
     * @ORM\ManyToMany(targetEntity="Vendor\App\Common\Entities\Attribute\Value")
     * @ORM\JoinTable(name="products_values",
    *      joinColumns={@ORM\JoinColumn(name="product_id", referencedColumnName="id")},
    *      inverseJoinColumns={@ORM\JoinColumn(name="value_id", referencedColumnName="id")}
     *      )
     * @JMS\Expose
     * @JMS\MaxDepth(2)
     * @JMS\Groups({"search"})
     * @JMS\Type("ArrayCollection<Vendor\App\Common\Entities\Attribute\Value>")
     */
    protected $attributeValues;

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

    /**
     * @return ArrayCollection
     */
    public function getAttributeValues() {
        return $this->attributeValues;
    }

    /**
     * @param ArrayCollection $attributeValues
     */
    public function setAttributeValues($attributeValues) {
        $this->attributeValues = $attributeValues;
    }

    /**
     * @param Value $attributeValue
     */
    public function addAttributeValue($attributeValue) {
        $this->attributeValues->add($attributeValue);
    }

    /**
     * @param Value $attributeValue
     */
    public function removeAttributeValue($attributeValue) {
        $this->attributeValues->removeElement($attributeValue);
    }

}

This is my Value entity that should be deserialized in the ArrayCollection:

<?php

namespace Vendor\App\Common\Entities\Attribute;

use Vendor\App\Common\Entities\AbstractEntity,
    Doctrine\ORM\Mapping as ORM,
    JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Entity
 * @ORM\Table(name="attribute_value")
 * @JMS\ExclusionPolicy("all")
 */
class Value extends AbstractEntity {

    /**
     * @var int $id
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @JMS\Expose
     * @JMS\Groups({"search"})
     * @JMS\Type("integer")
     */
    protected $id;

    /**
     * @var string $title
     * @ORM\Column(name="title", type="string", nullable=false)
     * @JMS\Expose
     * @JMS\Groups({"search"})
     * @JMS\Type("string")
     */
    protected $title;

    /**
     * @var int $attributeId
     * @ORM\Column(name="attribute_id", type="integer", nullable=false)
     * @JMS\Expose
     * @JMS\Groups({"search"})
     * @JMS\Type("integer")
     */
    protected $attributeId;

    /**
     * OWNING SIDE
     * @var \Vendor\App\Common\Entities\Attribute $attribute
     * @ORM\ManyToOne(targetEntity="Vendor\App\Common\Entities\Attribute", inversedBy="values")
     * @ORM\JoinColumn(name="attribute_id", referencedColumnName="id")
     * @JMS\Expose
     * @JMS\Groups({"search"})
     * @JMS\Type("Vendor\App\Common\Entities\Attribute")
     */
    protected $attribute;

    //Getters and setters ...

}

Just trying to simply deserialize the entity:

$serializer = SerializerBuilder::create()->build();
$entity = $serializer->deserialize($sourceJson, Product::class, 'json');

But the attributeValue ArrayCollection stays empty. What am I missing?


Solution

  • I found the solution. JMS has a default naming strategy which converts camelcase to underscore notation. Default is that the naming strategy either looks for the annotation @SerializedName and if that is not set, it will convert CamelCase to underscore.

    So the property just got ignored because it didn't match the expected name. Of course, it would be better if there was an error or a notification which gives a hint here on where to search for the problem (something like unknown property would have been nice).

    $serializer = SerializerBuilder::create()->setPropertyNamingStrategy(new IdenticalPropertyNamingStrategy())->build();
    

    Was the solution.