halaxa / json-machine

Efficient, easy-to-use, and fast PHP JSON stream parser
Apache License 2.0
1.08k stars 65 forks source link

Feature Request: Parse directly into a object of a PHP class #113

Closed tacman closed 3 months ago

tacman commented 3 months ago

What a cool library, thanks for releasing it! At first I thought I'd use it just when the JSON didn't fit in memory, but now I'm looking at replacing all of my $data = json_decode(file_get_contents('data.json')) with this.

I've used https://sabre.io/xml/reading/ to parse large XML files. I quite like the way I can define a php class and the parser will objects of that class (see the Custom element parsers section). I'm wondering if that's someone you've considered, a custom decoder based on a PHP class.

class Fruits 
{
    /** @var array<Fruit> $fruits */
    public array $fruits=[];
}

class Fruit
{  
     public string $name;
     public string $color;
    /** @var array<Harvest> $harvests */
     public array $harvests=[];

     public function getHarvests(): iterator
     {
          return $this->harvests;
     }
}

class Harvest
{
    var int $year;
    var int $poundsCollected;
}

Then parse into directly into the class objects

$fruits = Items::fromFile('fruits.json', Fruits::class);

foreach ($fruits as $fruit) {
    assert($fruit::class == Fruit::class);
    foreach ($fruit->getHarvests() as $harvest) {
         assert($harvest::class == Harvest::class);
         dd($harvest->year);
   }
}

I've found this particularly powerful when parsing nested documents, as they'd come back as a completely populated as class objects.

Thanks for your consideration.

halaxa commented 3 months ago

Hello and thank you for your suggestion and positive feedback on the library!

While your proposal for a custom decoder based on PHP classes is interesting, I currently don't plan to implement this feature directly into the library. I aim to keep the library focused on its core purpose - efficient reading and processing of large JSON files.

tacman commented 3 months ago

Any suggestions on how to implement it? Symfony now has several tools for mapping data to objects, mostly the serializer component but also the PropertyAccessor component and MapPayloadRequest (https://symfony.com/doc/current/controller.html#mapping-request-payload)

So I'm guessing there's a way in your parser to listen for a particular node/subtree and do the mapping there. Or maybe simply iterate through the nodes and deserialize the objects. Hmm.

halaxa commented 3 months ago

In such cases, I personally like to use no mapper libraries, but to simply create your own objects with accessor methods. Then pass the data (array, object) into the constructor. To me, it has many benefits that outweigh possible downsides.

tacman commented 3 months ago

Thanks. I was hoping to automate it a bit more with a library. The serializer will probably work.

halaxa commented 3 months ago

Sure. Each to their own ;)