serializer icon indicating copy to clipboard operation
serializer copied to clipboard

Type error in `UnionHandler`

Open dmaicher opened this issue 1 year ago • 9 comments

Q A
Bug report? yes
Feature request? yeso
BC Break report? yes
RFC? no

Steps required to reproduce the problem

jms/serializer        3.31.1 Library for (de-)serializing data of any complexity; supports XML, and JSON.
jms/serializer-bundle 5.5.1  Allows you to easily serialize, and deserialize data of any complexity
$serializer = SerializerBuilder::create()->build();
$serializer->serialize(new class() {
    public array|int $foo = [1, 2, 3];
}, 'json');

Expected Result

  • Should work fine

Actual Result

TypeError:
get_class(): Argument #1 ($object) must be of type object, array given

  at vendor/jms/serializer/src/Handler/UnionHandler.php:56

dmaicher avatar Nov 06 '24 13:11 dmaicher

It looks like arrays are not supported in current implementation at all. I will take a look at it. Looks like @simPod already started some work around that. https://github.com/schmittjoh/serializer/pull/1552

scyzoryck avatar Nov 08 '24 10:11 scyzoryck

I can confirm this issue. Unfortunately, it breaks existing code. For the time being, we had to pin jms/serializer to v3.30.0 because of this. It might be a good idea to release a new version reverting these changes until the feature supports arrays. (Or, as I see the issue with that, to specify a workaround for disabling the new feature.)

#1552 isn’t working on this, I think. The MR is about “unions” (not really) in arrays, not arrays in unions.

mermshaus avatar Nov 11 '24 12:11 mermshaus

Unfortunately, it breaks existing code

@scyzoryck would it make sense to revert the union types thing that is causing this error?

from my understanding, https://github.com/schmittjoh/serializer/pull/1546 is the guilty one, right?

goetas avatar Nov 12 '24 09:11 goetas

We also I think have 2 MRs on top of #1546 so it might need some additional reverts. As an example: #1563 Current issues with Union Types:

  • High prio: Support for true and false types. MR: #1569 - we need to add some
  • High prio: Support for array type - I believe I can finish the basic support (fix the issue from this issue), #1552 may add complex types later.
  • Medium/Low prio: It looks like there might be some issues with DepthExclusionStrategy for Union types, but I guess it was not working in the past at all.

Soo, personally I would go with fixing the issues, and going with the fix release this week with support for missing types.

scyzoryck avatar Nov 12 '24 15:11 scyzoryck

Soo, personally I would go with fixing the issues, and going with the fix release this week with support for missing types.

:+1:

goetas avatar Nov 14 '24 19:11 goetas

@dmaicher could you test newest dev-master version, please? :) If it will look good for you I will make the new fix release.

scyzoryck avatar Dec 03 '24 22:12 scyzoryck

@scyzoryck with my example code mentioned in the issue description I get

PHP Warning: Array to string conversion in /var/www/app/symfony/vendor/jms/serializer/src/Handler/UnionHandler.php on line 129

because in my case int is checked first it seems.

Maybe testPrimitive can be adjusted like this

    private function testPrimitive(mixed $data, string $type, string $format): bool
    {
        if ($type === 'array') {
            return is_array($data);
        }

        if (!is_scalar($data)) {
            return false;
        }

        switch ($type) {
            case 'integer':
            case 'int':
                return (string) (int) $data === (string) $data;

            case 'double':
            case 'float':
                return (string) (float) $data === (string) $data;

            case 'bool':
            case 'boolean':
                return (string) (bool) $data === (string) $data;

            case 'string':
                return is_string($data);
        }

        return false;
    }

Then the warning is gone in my case.

Another issue I noticed:

If I change my property to public array|int|string $foo with string value "666" then its serialized into an integer {"foo":666}. Should be {"foo":"666"} though?

dmaicher avatar Dec 04 '24 09:12 dmaicher

Sooo, it looks like we need a bit more development on Unions.

I would propose the next steps:

  • add enableUnionSupport method similar to add enableEnumSupport - to bring back the state from the 3.30.x as a default one
  • continue work on the UnionSupport as a experimental one.

@goetas does it sounds good for you?

If I change my property to public array|int|string $foo with string value "666" then its serialized into an integer {"foo":666}. Should be {"foo":"666"} though?

Yes, I think you are right. I think we have "type recognition" logic to determine the correct type - soo "666" is parsed as 666 during both - serialization and deserialization at the moment. So it seems to be similar issue as #1573 #1575 - FYI @fabianerni

IMO we should have the following logic for the type choosing:

Serialization:

  • Keep the types all the time

Deserialization:

  • If the type is supported - keep the type - as example: "666" will be kept as string
  • If the type is not supported - try to guess the type - as example: if property do not support string, but it supports int|bool "1" will be deserialized as int.

scyzoryck avatar Dec 04 '24 16:12 scyzoryck

@scyzoryck adding enableUnionSupport makes sense, it seems that there is much more work than expected to have it working properly.

It would be good to revert https://github.com/schmittjoh/JMSSerializerBundle/pull/954 as well

goetas avatar Dec 05 '24 22:12 goetas

Hi @dmaicher!

The fix has been published to dev-master branch. Could you test it, please?

Best, Marcin.

scyzoryck avatar Apr 01 '25 19:04 scyzoryck

Thanks a lot for the update @scyzoryck. I tested it on my app and the problem is indeed fixed 👍

dmaicher avatar Apr 02 '25 11:04 dmaicher