Skip to content

Serializer: cannot deserialize object it has serialized #42412

Closed
@noofaq

Description

@noofaq

Symfony version(s) affected: tested on various versions including very latest 5.3.6

Description

I have an object which may contain list of other objects in one of properties. It is allowed to not have this list in this case it is set to null.

Use case scenario: null value has a meaning that some checks has to be performed, empty list has a meaning that there were no checks required to be performed.

When object with nulled list is serialized everything works properly, but when it is deserialized it throws InvalidArgumentException with message Data expected to be an array, null given..

How to reproduce

Main serialized class code:

<?php

namespace App\Model;

class Audit
{
    /** @var ?AuditResult[] */
    private $auditResults;

    /**
     * @return ?AuditResult[]
     */
    public function getAuditResults(): ?array
    {
        return $this->auditResults;
    }

    /**
     * @param ?AuditResult[] $auditResults
     */
    public function setAuditResults(?array $auditResults): void
    {
        $this->auditResults = $auditResults;
    }

    /**
     * @param AuditResult $auditResult
     */
    public function addAuditResult(AuditResult $auditResult): void
    {
        if (!is_array($this->auditResults)) {
            $this->auditResults = [];
        }
        $this->auditResults[] = $auditResult;
    }
}

Subclass (used in the list):

<?php

namespace App\Model;

class AuditResult
{
    private string $check;

    /**
     * @param string $check
     */
    public function __construct(string $check)
    {
        $this->check = $check;
    }


    /**
     * @return string
     */
    public function getCheck(): string
    {
        return $this->check;
    }

    /**
     * @param string $check
     */
    public function setCheck(string $check): void
    {
        $this->check = $check;
    }


}

Example code to show the issue:

    public function test()
    {
        $audit = new Audit();
        $audit->setAuditResults([new AuditResult('check1'), new AuditResult('check2')]);

        dump($audit);
        dump($serializedAudit = $this->serializer->serialize($audit, 'json'));
        dump($this->serializer->deserialize($serializedAudit, Audit::class, 'json'));

        $audit = new Audit();
        dump($audit);
        dump($serializedAudit = $this->serializer->serialize($audit, 'json'));
        dump($this->serializer->deserialize($serializedAudit, Audit::class, 'json'));

    }

Results:
obraz

Possible Solution

Not found.

I have not yet found a correct solution. Playing with various combinations of PhpDoc / Type Hint annotations I am able to prevent the code to fail with exception, but in the same time it prevents correct deserialization of the items in the list to objects.

Same code worked in 4.* branch of symfony so I expect that there might be some breaking change in the meantime, but I was unable to pinpoint it.

I see visible behavior change by commenting Audit::addAuditResult method but it also prevents correct discovery of object type in the list.

Additional context

I have created configured skeleton Symfony application which is available here: https://github.com/noofaq/symfony-issue-42412
All changes to reproduce the issue are included in the very last commit.

Navigating to "/test" url should show the results.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions