softcreatr / jsonpath
JSONPath implementation for parsing, searching and flattening arrays
Requires
- php: ^8.3
- ext-json: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.92
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12
- squizlabs/php_codesniffer: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
README
This is a JSONPath implementation for PHP that targets portable RFC semantics and the JSONPath comparison consensus while keeping the API small, cached, and eval-free.
Highlights
- PHP 8.3+ with typed class constants, enums, readonly tokens,
#[Override], and noeval; CI covers PHP 8.3 through the PHP 8.6 nightly. - Works with arrays, objects, and
ArrayAccess/traversables in any combination. - Supports child and descendant selectors, wildcards, quoted name unions, indexes, negative indexes, and array slices.
- Filters support existence tests (including descendant and nested filters), path-to-path/root comparisons, deep equality, and distinct RFC
Nothingversus JSONnullsemantics. - Malformed and non-portable legacy syntax is rejected instead of being interpreted as a different valid query.
Installation
Requires PHP 8.3 or newer.
composer require softcreatr/jsonpath:"^2.0"
Development
Useful commands:
composer exec phpunit
composer phpstan
composer cs
JSONPath Examples
| JSONPath | Result |
|---|---|
$.store.books[*].author |
the authors of all books in the store |
$..author |
all authors |
$.store..price |
the price of everything in the store. |
$..books[2] |
the third book |
$..books[-1:] |
the last book in order. |
$..books[0,1] |
the first two books |
$..books['title','year'] |
multiple keys in a union |
$..books[:2] |
the first two books |
$..books[::2] |
every second book starting from first one |
$..books[1:6:3] |
every third book starting from 1 till 6 |
$..books[?(@.isbn)] |
filter all books with isbn number |
$..books[?(@.price<10)] |
filter all books cheaper than 10 |
$..* |
all elements in the data (recursively extracted) |
Expression syntax
| Symbol | Description |
|---|---|
$ |
The root object/element |
@ |
The current object/element inside a filter |
. or [] |
Child operator |
.. |
Recursive descent |
* |
Wildcard. All child elements regardless their index. |
[,] |
Array indexes or quoted member names as a set |
[start:end:step] |
Array slice operator borrowed from ES4/Python. |
?() |
Filters a node list by an existence or comparison expression |
PHP Usage
Using arrays
<?php require_once __DIR__ . '/vendor/autoload.php'; $data = ['people' => [ ['name' => 'Sascha'], ['name' => 'Bianca'], ['name' => 'Alexander'], ['name' => 'Maximilian'], ]]; print_r((new \Flow\JSONPath\JSONPath($data))->find('$.people.*.name')->getData()); /* Array ( [0] => Sascha [1] => Bianca [2] => Alexander [3] => Maximilian ) */
Using objects
<?php require_once __DIR__ . '/vendor/autoload.php'; $data = json_decode('{"name":"Sascha Greuel","birthdate":"1987-12-16","city":"Gladbeck","country":"Germany"}', false); print_r((new \Flow\JSONPath\JSONPath($data))->find('$')->getData()[0]); /* stdClass Object ( [name] => Sascha Greuel [birthdate] => 1987-12-16 [city] => Gladbeck [country] => Germany ) */
Magic method access
The options flag JSONPath::ALLOW_MAGIC will instruct JSONPath when retrieving a value to first check if an object
has a magic __get() method and will call this method if available. This feature is iffy and
not very predictable as:
- wildcard and recursive features will only look at public properties and can't smell which properties are magically accessible
- there is no
property_existscheck for magic methods so an object with a magic__get()will always returntruewhen checking if the property exists - any errors thrown or unpredictable behavior caused by fetching via
__get()is your own problem to deal with
<?php use Flow\JSONPath\JSONPath; $myObject = (new Foo())->get('bar'); $jsonPath = new JSONPath($myObject, JSONPath::ALLOW_MAGIC);
Filter expressions
Filter evaluation is intentionally limited to portable, eval-free operations:
- Comparisons support
==,!=,<,<=,>, and>=. - Logical expressions support
!,&&,||, and grouped subexpressions. - Singular current/root queries can be compared; non-singular, descendant, and nested-filter queries can be used as existence tests.
- Missing nodes use RFC
Nothingsemantics and are not conflated with JSONnull.
Examples:
[?(@.title == "A string")] // equality
[?(@.price < 10)] // numeric comparison
[?(@.key == @.other)] // path-to-path comparison
[?(@.key == $.rootValue)] // root reference
[?(@.isbn)] // singular existence test
[?(@..child)] // descendant existence test
[?(@.items[?(@.price > 10)])] // nested-filter existence test
[?(@['weird-key']=="ok")] // bracket-escaped member
Script expressions, synthetic .length, regular-expression/membership operators, array/object literals in comparisons, and the RFC function extensions (length(), count(), match(), search(), and value()) are not supported. Unsupported syntax raises JSONPathException.
A full list of comparison cases can be found in the JSONPath Comparison Cheatsheet.
Similar projects
FlowCommunications/JSONPath is the predecessor of this library by Stephen Frank
Other / Similar implementations can be found in the Wiki.
Changelog
A list of changes can be found in the CHANGELOG.md file.
License 🌳
This package is Treeware. If you use it in production, then we ask that you buy the world a tree to thank us for our work. By contributing to the ecologi project, you’ll be creating employment for local families and restoring wildlife habitats.
Contributors ✨
|
Sascha Greuel |
James Lucas |
Fabian Blechschmidt |
Mikko Pesari |
warlof |
Sergey G |
|
Alexandru Pătrănescu |
Oleg Andreyev |
Remy Suen |
esomething |