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 yourTest
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.