1

In a Symfony 5 project I get this error

Failed to denormalize attribute "options" value for class "App\Entity\Configuration": Expected argument of type "App\Entity\Option", "array" given at property path "options".

when trying to deserialize a JSON-string. The JSON-string looks like

{
  "options": [
    {
      "id": 1,
      "name": "x1"
    },
    {
      "id": 2,
      "name": "x2"
    }
  ]
}

The configuration Entity is

/**
 * @ORM\Entity(repositoryClass=ConfigurationRepository::class)
 */
class Configuration
{
    // ...

    /**
     * @ORM\OneToMany(targetEntity=Option::class, mappedBy="configuration", orphanRemoval=true)
     */
    private $options;
    
    // ...
}

and the Option Entity

/**
 * @ORM\Entity(repositoryClass=OptionRepository::class)
 * @ORM\Table(name="`option`")
 */
class Option
{
    // ...

    /**
     * @ORM\ManyToOne(targetEntity=Configuration::class, inversedBy="options")
     * @ORM\JoinColumn(nullable=false)
     */
    private $configuration;
    
    // ...
}

The serializer part is

// $configData set to the JSON-string near the top of this post
$encoder = new JsonEncoder();
$defaultContext = [
    AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $format, $context) {
        return $object->getName();
    },
];
$normalizer = new ObjectNormalizer(null, null, null, null, null, null, $defaultContext);
$serializer = new Serializer([$normalizer], [$encoder]);

$configurationDeserialized = $serializer->deserialize( $configData, Configuration::class, 'json' );

My test JSON-string was originaly created by using the Serializer to serialize an existing Configuration object using the settings stated here for the deserialization. So I guess I am missing some part of the serializer config that tells it how to handle the array -> OneToMany relation (which vice versa it automaticaly encodes)...

user3440145
  • 793
  • 10
  • 34
  • I'll assume, you should really add some type hints for Configuration.options ... like `/** @var Option[] */` – Jakumi Dec 30 '20 at 15:09
  • @Jakumi is a OneToMany relation a simple array? Not sure about that... – user3440145 Dec 30 '20 at 15:40
  • on deserialization/denormalization it is. an array of the many entities. you can also do `@var Collection – Jakumi Dec 30 '20 at 16:44
  • please show your adders/setters for the options – Jakumi Dec 30 '20 at 16:49
  • @Jakumi the annotation for getOptions() would be [at]return Collection|Option[] and the signature public function getOptions(): Collection. For addOption() it's public function addOption(Option $option): self – user3440145 Dec 30 '20 at 16:56
  • `@return Collection|Option[]` is somewhat unspecific, have you tried either `Collection – Jakumi Dec 30 '20 at 17:12
  • Do you also have a `setOptions()`-method? I assume the problem is, that the ObjectNormalizer only finds `addOption()` for setting the options and then passes the whole array instead of passing in each option separately. Can you confirm that it works when you have something like `{"options": {"id": 1, "name": "x1"}}`? If that works, check if adding a `setOptions(array $options)` works. When that is confirmed, I can look at the ObjectNormalizer and check how it uses the PropertyAccess, if its a bug, config changes are needed or anything like that to make it work without `setOptions()`. – dbrumann Dec 31 '20 at 07:03
  • @Jakumi I tried both variants on my getter which changed nothing - but didn't get the " (also for the property,..)" part. What do you mean by that? Also - why should changing the getter annotation help if this seems to be a setting/adding problem? – user3440145 Jan 03 '21 at 22:27
  • @dbrumannI tried a JSON version with your single object proposal - it changed the error to be "Expected argument of type "App\Entity\Option", "int" given..." ("int" instead of "array" now). So unfortunately I can't confirm it working with single a object. Adding a setOptions method also didn't help (did it like Jakumi posted in his linked SO-post). – user3440145 Jan 03 '21 at 22:30
  • @user3440145 property is the class variable so your `Configuration`'s `private $options`, where your `@ORM\OneToMany` already is set. there you should also set the type of that field, because the annotation reader might be *possibly* looking at that too. but I would have believed the adders are more relevant ;o/ – Jakumi Jan 04 '21 at 19:10
  • @user3440145 do you have a removeOption defined as well? because according to https://symfony.com/doc/current/components/property_access.html#writing-to-array-properties both methods must be defined. just to be sure though, please post your adders/setters/removers/getters (please, append to your question text) – Jakumi Jan 04 '21 at 19:13

0 Answers0