schmittjoh / serializer

Library for (de-)serializing data of any complexity (supports JSON, and XML)
http://jmsyst.com/libs/serializer
MIT License
2.32k stars 589 forks source link

Is there a way to deserialize always into an array no matter whether it is an array or object in json? #1470

Open helvete opened 1 year ago

helvete commented 1 year ago
Q A
Bug report? no
Feature request? maybe?
BC Break report? no
RFC? yes

Hey gyus,

I want to ask whether there is a way to deserialize (using annotations) always into an array no matter whether the string to be deserialized is an array or object in json.

The reason for this is that our application using serializer (version 3.18.0) must accept json that is externally translated from xml. Since in XML it depends on a count of nested items whether they are being deserialized to an array or an object, we cannot know the type beforehand. I know of XmlList annotation, that does exactly that. But it does not have any effect on deserialization of json serialized structures.

I understan XmlList it is intended for xml format and so it is semantically correct to ignore the annotation for json, but I wonder how to achieve this for json format.

Steps required to reproduce the problem

  1. Have 2 json strings: a. {"a":{"name": "b"}} b. {"a": [{"name": "b"}, {"name": "c"}]}
  2. Have a DTO similar to this (provided here as one string for brevity, typically this would be split into two files):
    
    <?php

namespace ATest;

use JMS\Serializer\Annotation as JMS;

class NameObject { /**

class ATestObject { /**

Expected Result

To have a way to to specify via annotations to deserialize both an object or an array (of the same objects) always into an array.

Actual Result

On attempt to deserialize the first string (ad 1.a.) the process fails.

Disclaimer

I have both studied the docs and seached through web in order to find how to deal with this, albeit without success. Please forgive me should I have overlooked some obvious solution to this. :slightly_smiling_face:

scyzoryck commented 1 year ago

On attempt to deserialize the first string (ad 1.a.) the process fails. Is there any error message?

I guess one solution that comes to my mind is to use some fake type (similar to the one that we have for enum) and use onPreDeserialise event or some Handler to normalize data before passing to deserialization.

helvete commented 1 year ago

Hey, thanks for reply!

I don't remember the exact error message. I will add it here once I have a minute for debugging this more. Basically, the error describes, that the deserialization visitor tries to process the object as a hash (so each K->V pair as a separate array item) and then fails because values of the object being deserialized do not match the type defined in the annotation.

In the example above, it expects the string "b" to be of type NameObject.

scyzoryck commented 1 year ago

@helvete at the moment I do not see a way to achieve such behaviour without writing using custom type (it is not possible to use handlers for array type. Then you can create handler that will do the job - something like that:


    public function deserializeOptionalArray(
        DeserializationVisitorInterface $visitor,
        $data,
        array $type,
    ): \Iterator {
        $type['name'] = 'array';
        return $visitor->visitArray(array_is_list($data) ? $data : [$data], $type);
    }```