Wiktor
Wiktor

Reputation: 1

Serialize and deserialize objects with objects as property type using SymfonySerializer

Is there a way to normalize (serialize and deserialize) object using Symfony Serializer (ObjectNormalizer) but by properties, not get/set/is... methods?

There is ObjectNormalizer but it's using get/set/is... methods, not declared properties, I have a lot of classes with logic to get something by get method, and I don't want to serialize them.

There is also PropertyNormalizer It serialize objects the way I want, but It cannot deserialize objects with objects as property. I get an error:

TypeError : Cannot assign array to property App\Shop\PageManagement\Factory\PageRowConfig\AbstractSliderConfig::$ctaUrl of type ?App\Shop\DTO\Url\UrlField

TypeError : Cannot assign array to property Foo\Bar::$property of type ?Foo/Bar/PropertyObject

I want to deserialize object with properties typed as another objects using SymfonySerializer but normalize it by property not get/set/is... methods.

class User
{
    private string $name;

    private string $surname;

    /**
     * @var Address[]
     */
    private array $addresses;

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }

    public function getSurname(): string
    {
        return $this->surname;
    }

    public function setSurname(string $surname): void
    {
        $this->surname = $surname;
    }

    public function getAddresses(): array
    {
        return $this->addresses;
    }

    public function setAddresses(array $addresses): void
    {
        $this->addresses = $addresses;
    }

    public function getFullName(): string
    {
        return $this->name . ' ' . $this->surname;
    }

    public function getType(): string
    {
        return 'sometype';
    }

}

abstract class SimpleAddress
{
    private string $city;

    public function getCity(): string
    {
        return $this->city;
    }

    public function setCity(string $city): void
    {
        $this->city = $city;
    }
}

class Address extends SimpleAddress
{
    private AddressTypeEnum $addressType;
    private string $street;

    public function getAddressType(): AddressTypeEnum
    {
        return $this->addressType;
    }

    public function setAddressType(AddressTypeEnum $addressType): void
    {
        $this->addressType = $addressType;
    }

    public function getStreet(): string
    {
        return $this->street;
    }

    public function setStreet(string $street): void
    {
        $this->street = $street;
    }

    public function getFullAddress(): string
    {
        return $this->getCity() . ' ' . $this->street;
    }
}

use MabeEnum\Enum;
use MabeEnum\EnumSerializableTrait;

class AddressTypeEnum extends Enum implements \Serializable
{
    use EnumSerializableTrait;

    public const WORK = 'Work';
    public const HOME = 'Home';

    public static function Work(): self
    {
        return self::get(self::WORK);
    }

    public static function HOME(): self
    {
        return self::get(self::HOME);
    }
}

And this is the result I want to get.

{
    "user": {
        "name": "Tom",
        "surname": "Tailor",
        "addresses": [
            {
                "addressType": "Work",
                "city": "LA",
                "street": "Central"
            },
            {
                "addressType": "Home",
                "city": "Washington",
                "street": "Some street"
            }
        ]
    }
}

And I can serialize it by PropertyNormalizer,but i cannot deserialize.

Upvotes: 0

Views: 995

Answers (1)

Bademeister
Bademeister

Reputation: 866

Your example works for me. No TypeError. So I wonder if I have understood your challenge.

I need to remove "user": {} so I can test it. The property user seems to be part of another object.

$json = '{
    "name": "Tom",
    "surname": "Tailor",
    "addresses": [
        {
            "addressType": "Work",
            "city": "LA",
            "street": "Central"
        },
        {
            "addressType": "Home",
            "city": "Washington",
            "street": "Some street"
        }
    ]
}';

That's how I deserialize.

$serializer->deserialize($json, User::class, 'json');

The addresses are added as a multi-dimensional array:

enter image description here

If I add User::class addAddresses(), I have a collection of Addresses::class.

public function addAddresses(Address $address): void
{
    $this->addresses[] = $address;
}

enter image description here

In the ReflectionExtractor (getTypes()), an attempt is made to resolve the type hint in a order.

The type is taken from addAddresses() and then the collection is set via setAddresses().

Upvotes: 0

Related Questions