To implement a custom source, we need to extend `Source` and implement 3 methods:
```php
use Cerbero\JsonParser\Sources\Source;
use Traversable;
class CustomSource extends Source
{
public function getIterator(): Traversable
{
// return a Traversable holding the JSON source, e.g. a Generator yielding chunks of JSON
}
public function matches(): bool
{
// return TRUE if this class can handle the JSON source
}
protected function calculateSize(): ?int
{
// return the size of the JSON in bytes or NULL if it can't be calculated
}
}
```
The parent class `Source` gives us access to 2 properties:
-`$source`: the JSON source we pass to the parser, i.e.: `new JsonParser($source)`
-`$config`: the configuration we set by chaining methods, e.g.: `$parser->pointer('/foo')`
The method `getIterator()` defines the logic to read the JSON source in a memory-efficient way. It feeds the parser with small pieces of JSON. Please refer to the [already existing sources](https://github.com/cerbero90/json-parser/tree/master/src/Sources) to see some implementations.
The method `matches()` determines whether the JSON source passed to the parser can be handled by our custom implementation. In other words, we are telling the parser if it should use our class for the JSON to parse.
Finally, `calculateSize()` computes the whole size of the JSON source. It's used to track the [parsing progress](#progress), however it's not always possible to know the size of a JSON source. In this case, or if we don't need to track the progress, we can return `null`.
If you find yourself implementing the same custom source in different projects, feel free to send a PR and we will consider to support your custom source by default. Thank you in advance for any contribution!
A JSON pointer is a [standard](https://www.rfc-editor.org/rfc/rfc6901) used to point to nodes within a JSON. This package leverages JSON pointers to extract only some sub-trees from large JSONs.
Consider [this JSON](https://randomuser.me/api/1.4?seed=json-parser&results=5) for example. To extract only the first gender and avoid parsing the rest of the JSON, we can set the `/0/gender` pointer:
> ⚠️ Please avoid intersecting pointers (e.g. setting both `/foo` and `/foo/bar`) as the deeper pointer won't be found and will force the parser to parse the whole JSON.
We can also specify a callback to execute when JSON pointers are found. This is handy when we have different pointers and we need to run custom logic for each of them:
// 2nd iteration: $key === 'country', $value instanceof Country
// and so on for all the objects in the array...
}
```
If the callbacks are enough to handle the pointers and we don't need to run any common logic for all pointers, we can avoid to manually call `foreach()` by chaining the method `traverse()`:
Otherwise if some common logic for all pointers is needed but we prefer methods chaining to manual loops, we can pass a callback to the `traverse()` method:
[Simdjson is faster](https://github.com/crazyxman/simdjson_php/tree/master/benchmark#run-phpbench-benchmark) than `json_decode()` and can be installed via `pecl install simdjson` if your server satisfies the [requirements](https://github.com/crazyxman/simdjson_php#requirement).
If we need a decoder that is not supported by default, we can implement our custom one.
<details><summary><b>Click here to see how to implement a custom decoder.</b></summary>
To create a custom decoder, we need to implement the `Decoder` interface and implement 1 method:
```php
use Cerbero\JsonParser\Decoders\Decoder;
use Cerbero\JsonParser\Decoders\DecodedValue;
class CustomDecoder implements Decoder
{
public function decode(string $json): DecodedValue
{
// return an instance of DecodedValue both in case of success or failure
}
}
```
The method `decode()` defines the logic to decode the given JSON value and it needs to return an instance of `DecodedValue` both in case of success or failure.
To make custom decoder implementations even easier, JSON Parser provides an [abstract decoder](https://github.com/cerbero90/json-parser/tree/master/src/Decoders/AbstractDecoder.php) that hydrates `DecodedValue` for us so that we just need to define how a JSON value should be decoded:
```php
use Cerbero\JsonParser\Decoders\AbstractDecoder;
class CustomDecoder extends AbstractDecoder
{
protected function decodeJson(string $json): mixed
{
// decode the given JSON or throw an exception on failure
To see some implementation examples, please refer to the [already existing decoders](https://github.com/cerbero90/json-parser/tree/master/src/Decoders).
If you find yourself implementing the same custom decoder in different projects, feel free to send a PR and we will consider to support your custom decoder by default. Thank you in advance for any contribution!
Not all JSONs are valid, some may present syntax errors due to an incorrect structure (e.g. `[}`) or decoding errors when values can't be decoded properly (e.g. `[1a]`). JSON Parser allows us to intervene and define the logic to run when these issues occur:
```php
use Cerbero\JsonParser\Decoders\DecodedValue;
use Cerbero\JsonParser\Exceptions\SyntaxException;
$json->progress()->current(); // the already parsed bytes e.g. 86759341
$json->progress()->total(); // the total bytes to parse e.g. 182332642
$json->progress()->fraction(); // the completed fraction e.g. 0.47583
$json->progress()->percentage(); // the completed percentage e.g. 47.583
$json->progress()->format(); // the formatted progress e.g. 47.5%
```
The total size of a JSON is calculated differently depending on the [source](#sources). It is not always possible to determine how large a JSON is, in these cases only the current progress is known: