Crell / Serde

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for integer-keyed dictionaries

Physikbuddha opened this issue · comments

Hello,

I just tried out your new library, and I'm very pleased about the ease of use and functionality. Love it!

However, there was one thing I couldn't realize:
Serializing a simple array of objects into JSON.

First try: Passing the array into the serializer, but I can't. Serde is only accepting objects.
Would be great to be able to serialize arrays as well, but this might come with drawbacks I'm not aware of.

Okay, so my next try was creating a wrapper class to solve this. I'm not the biggest fan of having an extra class just for serialization, but here we go:

class FooCollection
{
    /**
     * @param Foo[] $foos
     */
    public function __construct(#[DictionaryField(arrayType: Foo::class)] public array $foos)
    {
    }
}

Now here's the catch: My array is keyed with Unix timestamps (stored as integer):

$foos = [
    1658311200 => new Foo(),
    1658314800 => new Foo(),
    1658318400 => new Foo(),
    1658322000 => new Foo(),
    ...
];

$serde->serialize(new FooCollection($foos), 'json');

I can't use SequenceExporter, because it's expecting the array to be a list. DictionaryExporter on the other hand expects the array to be keyed with strings and is failing with an exception:

Crell\Serde\Attributes\Field::create(): Argument #1 ($serializedName) must be of type string, int given, called in vendor\crell\serde\src\PropertyHandler\DictionaryExporter.php on line 30

Is there something I'm missing, or an easy solution for this? Of course, I can walk through the whole array, convert the integer keys to strings, and do the whole thing again in the opposite direction when deserializing. But that doesn't feel great, so I want to avoid it.

Greetings.

There's two parts here.

  1. Numeric dictionaries should be allowed. I actually had that mostly done in a branch where I was working on allowing dictionaries to restrict their key type. Thanks for the reminder. 😄 I've finished that and merged it to master, so that should work as of the next release. (Or run dev-master for the moment.)

  2. Serializing arrays of objects is on the todo list, but not yet supported. Mainly, I'm not sure if the best way to do it. It may be a separate "entry point" object, like SerdeList or something. It also ties into the desire for streaming support. I'm still unsure of the best way to do it, which is why I haven't done it yet.

I'm using dev-master with my array-wrapper class now, and everything's working great and smooth.
And honestly, the additional wrapper class is not too much of a hassle. Great to hear that array serialization is on the todo list, but no need to rush the features.

Thanks for the library and the quick response, Larry. ❤

I just tagged 0.5.0, so you should be safe to switch to that now.

commented

As of 0.5.0, a simple way to deserialize objects in an array is described below.

class Human {
  public int $height;
  public int $weight;
}

class HumanList {
  /** @var array<Human> */
  #[Field(flatten: true)]
  #[SequenceField(arrayType: Human::class)]
  public readonly array $human;
}

$json = <<<JSON
[
  {
    "height": 172,
    "weight": 67
  },
  {
    "height": 152,
    "weight": 51
  }
]
JSON;

$serde = new SerdeCommon();

/** @var HumanList $humanList */
$humanList = $serde->deserialize($json, from: "json", to: HumanList::class);

foreach ($humanList->human as $humanArray) {
  /** @var Human $human */
  $human = $serde->deserialize($humanArray, from: "array", to: Human::class);
  // code...
}