Crell / Serde

Robust Serde (serialization/deserialization) library for PHP 8.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Deserialize arrays of objects when used as values in dictionaries

hnrch02 opened this issue · comments

Hi, loving the library so far!

I'm struggling to properly deserialize dictionaries whose values are arrays of objects. This is where I'm at:

use Crell\Serde\Attributes as Serde;

class Note {
    public string $text;
}

class Test {
    /**
     * @var array<string, Note[]>
     */
    #[Serde\DictionaryField(arrayType: 'array')]
    public array $notes = [];
}

As the PHPDoc specifies, the $notes member of the Test class is supposed to be a dictionary with string keys and Note arrays as values.

I managed to get it to work by introducing a Notes intermediary class, which I can then specify as the arrayType like so:

class Notes {
    #[Serde\SequenceField(arrayType: Note::class)]
    public array $notes = [];
}

class Test {
    /**
     * @var array<string, Notes>
     */
    #[Serde\DictionaryField(arrayType: Notes::class)]
    public array $notes = [];
}

but this is not ideal, since it complicates the serialized output unnecessarily, and makes it harder to address the individual elements as I have to go through the Notes class. I also tried applying type maps, but I didn't get far with that either.

So my question is: Can I do this in a better way? Maybe I'm missing an obvious solution here, so I would be grateful if someone could point me in the right direction.

Oh hey, people are using this library! 😄

Unfortunately, I don't think what you're doing is possible directly. Serde doesn't know from the docblocks, only the attributes, and there's no way to document the type of the inner array. The deserializer will see the incoming array but have no idea that it's supposed to map it to something. Objects nested inside arrays are just not sufficiently self-describing.

Possible workarounds to try:

  • TypeMaps won't help here.
  • You could potentially use flattening to make the serialized output tidier. I haven't experimented with this particular use case, but Serde was designed to be as flexible as possible for dealing with legacy structures so it may work, depending on what you consider acceptable.
  • You could extend the ObjectImporter yourself and make a new one, and have it apply just to your Test class. Then hard code whatever additional logic you can make work for that structure.

Off hand, I suspect a custom importer is the most promising.