LeagueCSV

Versions

JSON conversion

Since version 9.10.0.

The JsonEncoder converts a collection of serializable structures into a JSON string payload. While PHP provides the JsonSerializable interface and the package TabularDataReader classes implement it, if the document to serialize is “too” big it will make your script run out of memory because the CSV needs to be handle in full in memory.

To resolve this issue, the package introduces a lightweight JSON encoder for collection. This converter is built to handle huge files in a memory efficient way via a minimalist public API.

The encoder works with the iterable structure only. If your collection also supports the JsonSerializable interface, its method will be ignored.

Settings

Prior to converting the records into a JSON payload, you may wish to configure how the encoding will preserve records information. As with the other converters included in the package, the JsonEncoder is immutable. Everytime you change a settings a new instance will be return without changing the current instance.

Json encoding options

public JsonEncoder::flags(int $flags): self

The method supports all the flags from json_encode. To make sure an exception is always thrown instead of substituting some unencodable values in some specific cases, the following flags are applied, by default:

JSON_THROW_ON_ERROR & ~JSON_PARTIAL_OUTPUT_ON_ERROR

The JSON_PARTIAL_OUTPUT_ON_ERROR flag MUST be explicitly set if you want its feature to be used during conversion. This behaviour deviates from the default behaviour from json_encode.

Please refer to the JSON constants page on the PHP website for more information.

You can access the current setting using the converter public readonly property flags.

use League\Csv\JsonEncoder;

$converter = JsonEncoder::create();
$converter->flags; // returns JSON_THROW_ON_ERROR & ~JSON_PARTIAL_OUTPUT_ON_ERROR;

Json conversion depth

public JsonEncoder::depth(int $depth): self

Just like, json_encode, it is possible to define the conversion depth variable. The method will throw an exception if the value is lesser than 1.

You can access the current setting using the converter public readonly property deopth.

use League\Csv\JsonEncoder;

$converter = JsonEncoder::create();
$converter->depth; // returns 512;

Collection offset process

public JsonEncoder::includeOffset(): self
public JsonEncoder::excludeOffset(): self

These methods tell the converter to include or exclude the iterable offset when converting the document. If the offset needs to be present in the returned JSON payload, the returned JSON payload will be an object whose members key is the collection offset and the member value the json serialized member value.

The default is to exclude the offset information.

You can access the current setting using the converter public readonly property preserveOffset.

use League\Csv\JsonEncoder;

$converter = JsonEncoder::create();
$converter->includeOffset; // returns false;

Conversion

public JSONConverter::encode(iterable $records): Generator<string>;
public JSONConverter::encodeToFile(iterable $records, SplFileObject $file): int;
public JSONConverter::encodeToStream(iterable $records, resource $stream): int;
public JSONConverter::encodeToPath(iterable $records, string $path, string $open_mode = 'w', resource $context = null): int;

All the methods accept an iterable object whose members MUST support being encoded using PHP’s json_encode function and returns either directly a Generator with the JSON payload being generated or saved in the designed persistence layer progressively.

use League\Csv\JsonEncoder;
use League\Csv\Statement;
use League\Csv\Reader;

$csv = Reader::createFromPath('path/to/files/prenoms.csv')
    ->setDelimiter(';')
    ->setHeaderOffset(0);

//We are using the CharsetConverter to avoid the JsonEncoder throwing an exception
//because the CSV file contains non-compatible utf8 characters and thus will fail
//json encoding if the charset converter is not used.

CharsetConverter::addTo($csv, 'iso-8859-15', 'utf-8');

$stmt = Statement::create()
    ->where(fn (array $record): bool => str_starts_with($record['prenoms'], 'Anaë'))
    ->orderBy(fn (array $recordA, array $recordB): int => $recordB['nombre'] <=> $recordA['nombre'])
    ->offset(0)
    ->limit(3)
;

$bytes = JsonEncoder::create()
    ->includeOffset()
    ->flags(JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK)
    ->encodeToStream($stmt->process($csv), STDOUT);

echo PHP_EOL, $bytes, ' bytes were written.', PHP_EOL;

// will output in your console the following
// {
//     "1": {
//         "prenoms": "Ana\u00eblle",
//         "nombre": 38,
//         "sexe": "F",
//         "annee": 2004
//     },
//     "2": {
//         "prenoms": "Ana\u00eblle",
//         "nombre": 31,
//         "sexe": "F",
//         "annee": 2005
//     },
//     "8": {
//         "prenoms": "Ana\u00eblle",
//         "nombre": 30,
//         "sexe": "F",
//         "annee": 2008
//     }
// }
// 356 bytes were written.

$bytes = JsonEncoder::create()->encodeToPath($csv, '/path/storage/file.json');
echo PHP_EOL, $bytes, ' bytes were written.', PHP_EOL;
// will output
// 612875 bytes were written.
// and save in your file the full JSON payload in a single line progressively
// [{"prenoms":"Aaron","nombre":"55","sexe":"M","annee":"2004"},...,{"prenoms":"Zohra","nombre":"6","sexe":"F","annee":"2012"}]