symfony/json-path
Evaluate JSONPath expressions in Symfony/PHP to query and extract data from JSON documents. Lightweight library with simple API for selecting nodes, filtering arrays, and retrieving values, useful for config parsing, API responses, and data transformation.
Installation
composer require symfony/json-path
No additional configuration is required—autoloading handles the rest.
First Use Case: Extracting Nested Data
use Symfony\Component\JsonPath\JsonPath;
$json = '{"user": {"profile": {"name": "John", "age": 30}}}';
$jsonPath = new JsonPath();
$name = $jsonPath->evaluate('$.user.profile.name', json_decode($json, true));
// Returns: "John"
Where to Look First
evaluate(): Single result (or null).evaluateAll(): Array of all matches.search(): Alias for evaluate() (Symfony-compatible).json_decode() or Laravel’s Http client for API responses.API Response Parsing
public function parseStripeResponse($response)
{
$data = json_decode($response->getBody(), true);
$amount = JsonPath::search($data, '$.data.object.amount');
$currency = JsonPath::search($data, '$.data.object.currency');
return [$amount, $currency];
}
Dynamic Query Building
public function getNestedValue($json, string $path, $default = null)
{
return JsonPath::search($json, $path) ?? $default;
}
// Usage:
$age = $this->getNestedValue($userJson, '$.user.profile.age', 0);
Filtering with Predicates
$expensiveItems = JsonPath::search($inventory, '$.items[?(@.price > 100)].name');
// Returns: ["Laptop", "Monitor"]
Laravel Service Container Binding
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(JsonPath::class, fn() => new JsonPath());
}
// Usage in controllers:
public function __construct(private JsonPath $jsonPath) {}
Validation Helper
public function validateRequiredFields($json, array $paths)
{
foreach ($paths as $path) {
if (empty(JsonPath::search($json, $path))) {
throw new \InvalidArgumentException("Missing required path: $path");
}
}
}
Middleware for API Responses:
public function handle($request, Closure $next)
{
$response = $next($request);
if ($response->isOk()) {
$data = json_decode($response->getBody(), true);
$response->setData(JsonPath::search($data, '$.transformed.path'));
}
return $response;
}
Eloquent JSON Casting:
use Illuminate\Database\Eloquent\Casts\Attribute;
public function transformedJson(): Attribute
{
return Attribute::make(
get: fn($value) => JsonPath::search($value, '$.user.profile.name'),
);
}
Artisan Commands for Data Extraction:
public function handle()
{
$json = file_get_contents('data.json');
$results = JsonPath::searchAll($json, '$.records[*].id');
$this->line('Extracted IDs: ' . implode(', ', $results));
}
Path Evaluation Quirks
$.store..book (recursive descent) may not work as expected in all JSON structures.evaluateAll() and filter results manually if needed:
$allBooks = JsonPath::searchAll($json, '$.store.*[*].book');
Case Sensitivity in Keys
$.User.Name fails if the JSON uses $.user.name.json_decode($json, true, 512, JSON_BIGINT_AS_STRING).Performance with Deep Nesting
$.a.b.c.d[*].e) can slow down evaluation.evaluateAll() for bulk operations.False Matches with Wildcards
*.book matches any object with a book key, not just top-level objects.$.store.book).Empty Results vs. null
evaluate() returns null for missing paths, while evaluateAll() returns an empty array.empty() checks for evaluateAll() and is_null() for evaluate().Log Path Evaluations:
try {
$result = JsonPath::search($json, $path);
Log::debug("Path [$path] resolved to: ", ['result' => $result]);
} catch (\Symfony\Component\JsonPath\Exception\JsonPathException $e) {
Log::error("Invalid JSONPath [$path]: " . $e->getMessage());
}
Validate JSON First:
if (json_validate($json)) {
$result = JsonPath::search($json, $path);
} else {
throw new \InvalidArgumentException("Invalid JSON provided");
}
Test Edge Cases:
// Test empty JSON
$this->assertNull(JsonPath::search([], '$.nonexistent'));
// Test arrays
$this->assertEquals([1, 2], JsonPath::search([1, 2], '$[*]'));
// Test nested arrays
$this->assertEquals([1, 2], JsonPath::search([['a', 1], ['b', 2]], '$[*][1]'));
Custom JSONPath Functions
contains()).$filtered = collect($json)->filter(fn($item) => str_contains($item, 'search'));
Laravel Facade
// app/Facades/JsonPath.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class JsonPath extends Facade { protected static function getFacadeAccessor() => 'jsonPath'; }
Register in AppServiceProvider:
$this->app->bind('jsonPath', fn() => new \Symfony\Component\JsonPath\JsonPath());
Usage:
$result = \App\Facades\JsonPath::search($json, '$.path');
Performance Optimization
$cacheKey = md5($json . $path);
return Cache::remember($cacheKey, now()->addMinutes(10), fn() =>
JsonPath::search($json, $path)
);
Integration with Laravel HTTP Client
Http client:
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Http;
Http::macro('jsonPath', function ($path) {
return JsonPath::search($this->json(), $path);
});
Usage:
$response = Http::get('https://api.example.com/data')->jsonPath('$.data.id');
How can I help you explore Laravel packages today?