0

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.

Wiktor
  • 1
  • 1
  • Have a look at the following article. It's about how to write and read only on property. Specially the section "PropertyNormalizer and custom PropertyTypeExtractor". You then have to drop this custom normaliser. https://dev.to/elevado/create-a-custom-symfony-normalizer-for-mapping-values-4nc2 – Hans FooBar May 26 '23 at 11:46
  • @HansWurst unfortunatelly it cannot handle array/collection of objects ;/ – Wiktor May 26 '23 at 16:18
  • Do you have an example of the DTO and the property (including getter and setter)? What exactly do you want to reach? – Hans FooBar May 26 '23 at 18:01
  • Where are you specifically blocked implementing SymfonySerializer? Can you create a much simpler [mre]? (the minimum code necessary to reproduce your scenario, it must not work flawless, it is fine if it works to demonstrate your issue). The result you show is JSON Text, which is AFAIK not configurable in PHP for `\Serializable`, are you sure you want to use SymfonySerializer and not JsonSerializable? (Just asking, no preference) – hakre May 29 '23 at 08:36
  • It is actually minimal repoducible example. I wan't to deserialize it to object's again, so yes i want to use Symfony serializer. I describe all in my question, i belive you read all before write this comment. – Wiktor May 29 '23 at 09:01

1 Answers1

0

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().

Hans FooBar
  • 149
  • 11