PHPackages                             diamond-dove/simple-json - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. diamond-dove/simple-json

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

diamond-dove/simple-json
========================

Read and write big JSON files

v2.0.3(2mo ago)3231MITPHPPHP ^8.4CI passing

Since Feb 12Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/diamond-dove/simple-json)[ Packagist](https://packagist.org/packages/diamond-dove/simple-json)[ Docs](https://github.com/diamond-dove/simple-json)[ RSS](/packages/diamond-dove-simple-json/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (7)Dependencies (14)Versions (8)Used By (0)

Simple JSON File Reader and Writer
==================================

[](#simple-json-file-reader-and-writer)

[![Latest Version on Packagist](https://camo.githubusercontent.com/ab352ebf7f09de346ffa6f392d06df68397259e40cb9df81d1ae47499502d7fa/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6469616d6f6e642d646f76652f73696d706c652d6a736f6e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/diamond-dove/simple-json)[![Tests](https://camo.githubusercontent.com/c380954838660dec481040a8a0116ed709998a786b73ca2a0c9e9aae2af235ca/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6469616d6f6e642d646f76652f73696d706c652d6a736f6e2f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/diamond-dove/simple-json/actions/workflows/tests.yml)[![Total Downloads](https://camo.githubusercontent.com/3594087e2df9232f631cd25e947e04d151e321253151c2c7fc58ec5807784a63/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6469616d6f6e642d646f76652f73696d706c652d6a736f6e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/diamond-dove/simple-json)[![License](https://camo.githubusercontent.com/e576972cdf9debcf9316538fb2ed37099db205abb6b4a890e808f6ac7805695c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6469616d6f6e642d646f76652f73696d706c652d6a736f6e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![GitHub Stars](https://camo.githubusercontent.com/a484b6b73392ea3e675a1be06828ec09c79c68bc8e063a1b813bb0e475f95f69/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6469616d6f6e642d646f76652f73696d706c652d6a736f6e3f7374796c653d666c61742d737175617265266c6f676f3d676974687562)](https://github.com/diamond-dove/simple-json/stargazers)

This package makes it easy to read and write simple JSON files. It uses generators to minimize memory usage, even when dealing with large files.

> ⭐ **Enjoying this package?** If it saves you time, please consider [giving it a star on GitHub](https://github.com/diamond-dove/simple-json) — it helps other developers discover it and motivates continued work. Thank you!

Here is an example of how to read a JSON file:

```
use DiamondDove\SimpleJson\SimpleJsonReader;

SimpleJsonReader::create('users.json')->get()
   ->each(function(array $user) {
        // process the row
    });
```

Requirements
============

[](#requirements)

- PHP 8.4 or higher

Installation
============

[](#installation)

You can install the package using composer:

```
composer require diamond-dove/simple-json

```

Usage
=====

[](#usage)

Reading a JSON
--------------

[](#reading-a-json)

Suppose you have a JSON file with the following content:

```
[
  {"email":  "john@example.com", "first_name":  "John"},
  {"email":  "jane@example.com", "first_name":  "jane"}
]
```

To read this file in PHP, you can do the following:

```
use DiamondDove\SimpleJson\SimpleJsonReader;

// $records is an instance of Illuminate\Support\LazyCollection
$records = SimpleJsonReader::create($pathToJson)->get();

$records->each(function(array $user) {
   // in the first pass $user will contain
   // ['email' => 'john@example.com', 'first_name' => 'john']
});
```

### Working with LazyCollections

[](#working-with-lazycollections)

`get` will return an instance of `Illuminate\Support\LazyCollection`. This class is part of the Laravel framework. Behind the scenes generators are used, so memory usage will be low, even for large files.

You'll find a list of methods you can use on a `LazyCollection` [in the Laravel documentation.](https://laravel.com/docs/master/collections#the-enumerable-contract)

Here's a quick, silly example where we only want to process rows that have a first\_name that contains more than 5 characters. You'll find a list of methods you can use on a LazyCollection in the Laravel documentation.

Here's a quick, silly example where we only want to process elements that have a first\_name that contains more than 5 characters.

```
SimpleJsonReader::create($pathToJson)->get()
->filter(function(array $user) {
return strlen($user['first_name']) > 5;
})
->each(function(array $user) {
// processing user
});
```

### Reading from a string or a stream

[](#reading-from-a-string-or-a-stream)

You don't have to read from a file. If you already have the JSON in a string, or in an open stream resource, you can read from it directly:

```
use DiamondDove\SimpleJson\SimpleJsonReader;

// From a string
SimpleJsonReader::createFromString('[{"name": "John"}, {"name": "Jane"}]')
    ->get()
    ->each(function (array $user) {
        // process the row
    });

// From an open stream resource (you keep ownership of the resource)
$stream = fopen('php://temp', 'r+b');
fwrite($stream, '[{"name": "John"}]');

SimpleJsonReader::createFromResource($stream)->get()->each(/* ... */);

fclose($stream);
```

Writing files
-------------

[](#writing-files)

To write a JSON file, you can use the following code:

```
use DiamondDove\SimpleJson\SimpleJsonWriter;

$writer = SimpleJsonWriter::create($pathToJson)
    ->push([
        [
            'first_name' => 'John',
            'last_name' => 'Doe',
        ],
        [
            'first_name' => 'Jane',
            'last_name' => 'Doe',
        ],
    ]);
```

The file at pathToJson will contain:

```
[
  {"first_name": "John", "last_name": "Doe"},
  {"first_name":  "Jane", "last_name":  "Doe"}
]
```

You can also use:

```
SimpleJsonWriter::create($this->pathToJson)
                        ->push([
                            'name'  => 'Thomas',
                            'state' => 'Nigeria',
                            'age'   => 22,
                        ])
                        ->push([
                            'name'  => 'Luis',
                            'state' => 'Nigeria',
                            'age'   => 32,
                        ]);
```

In-memory JSON toolkit
======================

[](#in-memory-json-toolkit)

Besides streaming whole files, the package ships a small, framework-agnostic toolkit for working with a single JSON document in memory: safe parsing, dot-path access with strict typed extraction, and validation — all via the static `Json` facade, with zero extra dependencies.

Safe parsing &amp; typed access
-------------------------------

[](#safe-parsing--typed-access)

`Json::parse()` decodes with `JSON_THROW_ON_ERROR` and, on malformed JSON, throws a `DiamondDove\SimpleJson\Exceptions\InvalidJsonException` (the native `JsonException` is chained as `$previous`) — so you never have to second-guess `json_decode()`'s ambiguous `null` return. Use `Json::tryParse()` for a `null`-on-failure variant that never throws.

```
use DiamondDove\SimpleJson\Json;

$json = '{"user": {"name": "Ana", "age": 30, "address": {"city": "Santo Domingo"}}}';

$city = Json::parse($json)->path('user.address.city')->string();   // 'Santo Domingo'
$age  = Json::parse($json)->path('user.age')->int();               // 30

// Exception-free
$accessor = Json::tryParse($maybeJson);   // null when the JSON is invalid
```

`path()` walks dot-notation (case-sensitive) and returns another accessor. The typed terminals come in three flavours:

```
$user = Json::parse($json)->path('user');

$user->path('name')->string();          // strict: throws JsonTypeException on a type mismatch
$user->path('age')->int();              // strict int (rejects 30.0 and "30")
$user->path('nickname')->stringOr('—'); // lenient: returns the default when missing/mismatched
$user->path('nickname')->stringOrNull();// lenient: returns null when missing/mismatched
$user->path('age')->isPresent();        // true — distinguishes a present null from a missing key
```

Terminals are **strict — no silent coercion**: `int` requires a real integer, `float`widens an integer to float (`5` → `5.0`), `bool` rejects `0`/`1`. The full set is `string`, `int`, `float`, `bool`, `array`, each with `*Or($default)` and `*OrNull()`variants.

> **Limitation:** `path()` uses dot-notation; a JSON key that contains a literal dot (e.g. `"weird.key"`) is matched as a whole key first by the underlying resolver, while a genuinely nested `{"weird":{"key":...}}` is what dot-segmentation targets — avoid literal dots in keys you intend to traverse.

Validation
----------

[](#validation)

`Json::validate()` checks a JSON document against Laravel-style rules using a tiny in-house engine — **no `illuminate/validation`** and no other new dependency. Rules are written as pipe strings or arrays, and fields are addressed with the same dot-notation as `path()`.

```
use DiamondDove\SimpleJson\Json;

$result = Json::validate('{"email": "ana@example.com", "age": 30}', [
    'email'     => 'required|email',
    'age'       => 'int|min:18',
    'user.name' => 'required|string',   // dot-notation, case-sensitive
    'tags'      => ['nullable', 'array'],
]);

$result->passes();     // bool
$result->fails();      // bool
$result->errors();     // ['user.name' => ['The user.name field is required.']]
$result->validated();  // array of validated fields; throws JsonValidationException on failure
```

Supported rules: `required`, `nullable`, `string`, `int`, `numeric`, `bool`, `array`, `email`, `min`, `max`, `between`, `in`, `regex`. Type rules are **strict** (consistent with the accessor): `int`/`numeric` reject numeric strings, `bool` rejects `0`/`1`. `min`/`max`/`between` are inclusive and type-aware (number magnitude, string length, or array count). An unknown rule or a missing rule parameter throws `\InvalidArgumentException`(it's a programming error, not a validation failure).

Mapping to typed objects (DTOs)
-------------------------------

[](#mapping-to-typed-objects-dtos)

`Json::map()` hydrates a plain PHP class from a JSON string (or an already-decoded array) using constructor property promotion — no setters, no reflection-written private properties, and no heavy mapping dependency. It maps source keys to constructor parameters by name, recurses into nested DTOs, hydrates backed enums, and maps lists of DTOs via the `#[ListOf]` attribute.

```
use DiamondDove\SimpleJson\Json;
use DiamondDove\SimpleJson\Mapping\Attributes\ListOf;

enum Status: string {
    case Active = 'active';
    case Inactive = 'inactive';
}

final class Address {
    public function __construct(
        public readonly string $city,
        public readonly ?string $zip = null,
    ) {}
}

final class Tag {
    public function __construct(public readonly string $label) {}
}

final class User {
    public function __construct(
        public readonly string $name,
        public readonly int $age,
        public readonly ?Address $address = null,            // nested DTO
        public readonly Status $status = Status::Active,     // backed enum
        #[ListOf(Tag::class)] public readonly array $tags = [], // list of DTOs
    ) {}
}

$user = Json::map('{
    "name": "Ana", "age": 30,
    "address": {"city": "Santo Domingo"},
    "status": "active",
    "tags": [{"label": "vip"}, {"label": "beta"}]
}', User::class);

$user->address->city;   // 'Santo Domingo'
$user->status;          // Status::Active
$user->tags[0]->label;  // 'vip'
```

Mapping is **strict**, mirroring the rest of the toolkit: a type mismatch, a missing required parameter, or an invalid enum value throws a `DiamondDove\SimpleJson\Exceptions\JsonMappingException` (which also implements the `JsonException` marker, so a thrown DTO-constructor exception is wrapped and chained as `$previous`). Extra source keys are ignored.

Because `Json::map()` also accepts a decoded array, it composes directly with the streaming reader — hydrate every row of a huge file into typed objects without loading the whole file:

```
use DiamondDove\SimpleJson\SimpleJsonReader;

SimpleJsonReader::create('users.json')->get()
    ->map(fn (array $row) => Json::map($row, User::class))
    ->each(function (User $user) {
        // strongly-typed, one row at a time, low memory
    });
```

JSONPath queries (optional)
---------------------------

[](#jsonpath-queries-optional)

For queries that go beyond the core dot-notation `path()` — recursive descent, wildcards, array slices, filter expressions — `Json::query()` wraps the [`softcreatr/jsonpath`](https://github.com/SoftCreatR/JSONPath) package. It is an **optional** dependency: the toolkit core stays dependency-free, and you only pull it in if you need full JSONPath.

```
composer require softcreatr/jsonpath
```

```
use DiamondDove\SimpleJson\Json;

$json = '{"store": {"book": [
    {"title": "A", "price": 8.95},
    {"title": "B", "price": 12.99}
]}}';

Json::query($json, '$..book[?(@.price < 10)].title');  // ['A']
Json::query($json, '$.store.book[*].title');           // ['A', 'B']
Json::query($json, '$..nonexistent');                  // []  (empty match)
```

If the package is not installed, `Json::query()` throws a `DiamondDove\SimpleJson\Exceptions\MissingDependencyException` whose message tells you exactly what to install. An invalid JSONPath expression throws `JsonQueryException`, and malformed JSON throws `InvalidJsonException` — all three implement the `JsonException`marker, so you can catch them uniformly.

JSON Schema validation (optional)
---------------------------------

[](#json-schema-validation-optional)

When you already have a JSON Schema, `Json::validateSchema()` validates a document against it by wrapping the optional [`opis/json-schema`](https://github.com/opis/json-schema)package (multiple drafts). It complements the built-in rule engine.

```
composer require opis/json-schema
```

```
use DiamondDove\SimpleJson\Json;

$schema = '{"type":"object","required":["age"],"properties":{"age":{"type":"integer","minimum":0}}}';

Json::matchesSchema('{"age":30}', $schema);   // true
Json::matchesSchema('{"age":-1}', $schema);   // false

$result = Json::validateSchema('{"age":-1}', $schema);
$result->passes();   // false
$result->errors();   // ['/age: Number must be greater than or equal to 0']
```

Remote `$ref`s are **not** fetched (no network access), so the validator is safe against SSRF. A structurally invalid schema throws `JsonSchemaException`; a document that simply doesn't conform is reported through the result (not an exception).

Advanced typed mapping (optional)
---------------------------------

[](#advanced-typed-mapping-optional)

`Json::map()` covers plain DTOs. For advanced type signatures it can't express — `list`, `int`, `non-empty-string`, shaped arrays, generics — `Json::mapTo()`wraps the optional [`cuyz/valinor`](https://github.com/CuyZ/Valinor) mapper.

```
composer require cuyz/valinor
```

```
use DiamondDove\SimpleJson\Json;

Json::mapTo('["a", "b", "c"]', 'list');                 // ['a', 'b', 'c']
Json::mapTo('{"name": "Ana", "age": 30}', 'array{name: string, age: int}');
Json::mapTo('200', 'int');   // throws JsonMappingException (out of range)
```

Data that violates the signature throws `JsonMappingException`; an invalid signature is a programming error and throws `\InvalidArgumentException`.

Testing
=======

[](#testing)

```
composer test       # run the test suite
composer analyse    # run PHPStan static analysis
composer format     # apply the code style fixes
```

Changelog
=========

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for details on what has changed recently.

Contributing
============

[](#contributing)

Please see [CONTRIBUTING](https://github.com/diamond-dove/simple-json/blob/main/CONTRIBUTING.md) for details.

Security
========

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Show your support
=================

[](#show-your-support)

If this package is useful to you, please ⭐ **[star it on GitHub](https://github.com/diamond-dove/simple-json)**. It's the easiest way to support the project, helps others find it, and is genuinely appreciated.

Credits
=======

[](#credits)

- [Fermin Perdomo](https://github.com/masterfermin02)
- [All Contributors](../../contributors)

License
=======

[](#license)

The MIT License [(MIT)](LICENSE.md). Please see License File for more information.

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance87

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor1

Top contributor holds 96.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~262 days

Recently: every ~316 days

Total

7

Last Release

65d ago

Major Versions

v1.0.2 → v2.0.02025-06-24

PHP version history (3 changes)v1.0.0PHP ^8.0|^8.1

v1.0.1PHP ^8.0

v2.0.0PHP ^8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/17c416be607cbb47c55e76e115e3c35c3d74906ff422e808e4bba3be1dfb5cd6?d=identicon)[masterfermin02](/maintainers/masterfermin02)

---

Top Contributors

[![masterfermin02](https://avatars.githubusercontent.com/u/4625540?v=4)](https://github.com/masterfermin02 "masterfermin02 (31 commits)")[![elminson](https://avatars.githubusercontent.com/u/2476286?v=4)](https://github.com/elminson "elminson (1 commits)")

---

Tags

jsongeneratorstreaminglazylarge filesmemory-efficientjson-streamdiamond-dovesimple-json

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/diamond-dove-simple-json/health.svg)

```
[![Health](https://phpackages.com/badges/diamond-dove-simple-json/health.svg)](https://phpackages.com/packages/diamond-dove-simple-json)
```

###  Alternatives

[spatie/laravel-medialibrary

Associate files with Eloquent models

6.2k45.4M681](/packages/spatie-laravel-medialibrary)[spatie/laravel-health

Monitor the health of a Laravel application

88212.7M182](/packages/spatie-laravel-health)[sbsaga/toon

🧠 TOON for Laravel — a compact, human-readable, and token-efficient data format for AI prompts &amp; LLM contexts. Perfect for ChatGPT, Gemini, Claude, Mistral, and OpenAI integrations (JSON ⇄ TOON).

6665.2k](/packages/sbsaga-toon)[dragon-code/laravel-feeds

Fast export of large datasets to feeds for marketplaces and services

331.9k](/packages/dragon-code-laravel-feeds)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
