Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Json Path Laravel Package

symfony/json-path

Symfony JsonPath is a PHP component for navigating and querying JSON using JSONPath syntax per RFC 9535. Use JsonCrawler to evaluate paths, filter expressions, and extract values from complex JSON documents.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require symfony/json-path:^8.1.9
    

    No additional configuration is required—autoloading handles the rest. Pin to ^8.1.9: the 8.1.2–8.1.8 range contains escaping/anchor bugs in the match()/search() filter functions and drops null elements from index/slice selectors.

  2. 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"
    
  3. Where to Look First

    • RFC 9535: JSONPath Specification for syntax rules.
    • Core Methods:
      • evaluate(): Single result (or null).
      • evaluateAll(): Array of all matches.
      • search(): Alias for evaluate() (Symfony-compatible).
    • Changelog / Upgrade Notes (v8.1.9): three escaping-and-selector fixes that can change existing results—read the notes before bumping in a production app.
    • Laravel Integration: Pair with json_decode() or Laravel’s Http client for API responses.

Implementation Patterns

Usage Patterns

  1. 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];
    }
    
  2. 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);
    
  3. Filtering with Predicates

    $expensiveItems = JsonPath::search($inventory, '$.items[?(@.price > 100)].name');
    // Returns: ["Laptop", "Monitor"]
    
  4. Building Paths from User/DB-Supplied Keys

    // Since 8.1.9, backslashes and control characters in quoted keys are escaped correctly.
    // Use the library's escaping helper instead of hand-rolling quotes:
    $path = '$[' . JsonPath::key($columnName) . ']';   // safe for keys containing " \ or \n
    $value = $jsonPath->evaluate($path, $payload);
    

    Hand-concatenating "'$columnName'" still breaks on quotes, backslashes, and control characters—especially now that escaping is strict.

  5. Regex Predicates (match() vs search())

    // match(): anchored full-string match (RFC 9535)
    $isoDates = $jsonPath->evaluateAll($data, '$[?match(@.date, "[0-9]{4}-[0-9]{2}-[0-9]{2}")]');
    
    // search(): substring match anywhere in the string
    $authors = $jsonPath->evaluateAll($data, '$[?search(@.author, "Rob")]');
    
    // Slashes in patterns must be escaped; anchoring is now enforced as specified.
    $urls = $jsonPath->evaluateAll($data, '$[?search(@.url, "https:\\/\\/example\\.com")]');
    

    If a filter previously returned partial matches via match(), it will now return nothing—see Gotchas.

  6. Laravel Service Container Binding

    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->singleton(JsonPath::class, fn() => new JsonPath());
    }
    // Usage in controllers:
    public function __construct(private JsonPath $jsonPath) {}
    
  7. 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");
            }
        }
    }
    

Workflow Integration

  • 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));
    }
    
  • Upgrade Checklist for 8.1.9:

    composer update symfony/json-path --with-dependencies
    php artisan test --filter=JsonPath
    

    Re-run any test asserting on match() results, nullable array slices ($[0:3], $[*]), or paths built from arbitrary keys.


Gotchas and Tips

Pitfalls

  1. match() Is Anchored, search() Is Not (fixed in 8.1.9)

    • Issue: Before 8.1.9, anchoring was applied incorrectly, so a match() pattern could behave like a substring search. After upgrading, $[?match(@.id, "abc")] no longer matches "xabcx".
    • Fix: Use search() where you actually wanted a substring test, and wrap partial patterns explicitly:
      '$[?match(@.id, ".*abc.*")]'  // equivalent to the old (buggy) behaviour
      
  2. Regex Patterns Need Proper Slash Escaping

    • Issue: / inside match()/search() patterns must be escaped (\\/), otherwise the expression fails to parse or matches nothing (escaping fixed in 8.1.9, so previously-passing patterns may now be rejected).
    • Fix: Escape slashes and anchors deliberately; test patterns in isolation:
      $jsonPath->evaluateAll($data, '$[?search(@.url, "https:\\/\\/api\\.example\\.com")]');
      
  3. Index and Slice Selectors Now Preserve null (8.1.9)

    • Issue: $[*], $[0], and $[0:3] previously dropped null array elements. They are now returned.
    • Fix: If your code relied on nulls being filtered out, filter explicitly:
      $values = array_values(array_filter(
          $jsonPath->evaluateAll($data, '$.items[*]'),
          fn ($v) => $v !== null
      ));
      
  4. Quoted Keys with Backslashes/Control Characters

    • Issue: Manually building $['key'] breaks when the key contains ", \, or a newline/tab. Escaping of these was corrected in JsonPath::key() in 8.1.9.
    • Fix: Always delegate key escaping to the library rather than string interpolation.
  5. Path Evaluation Quirks

    • Issue: $.store..book (recursive descent) may not work as expected in all JSON structures.
    • Fix: Use evaluateAll() and filter results manually if needed:
      $allBooks = JsonPath::searchAll($json, '$.store.*[*].book');
      
  6. Case Sensitivity in Keys

    • Issue: $.User.Name fails if the JSON uses $.user.name.
    • Fix: Normalize keys or use case-insensitive JSON parsers like json_decode($json, true, 512, JSON_BIGINT_AS_STRING).
  7. Performance with Deep Nesting

    • Issue: Complex paths (e.g., $.a.b.c.d[*].e) can slow down evaluation.
    • Fix: Cache parsed JSON or use evaluateAll() for bulk operations.
  8. False Matches with Wildcards

    • Issue: *.book matches any object with a book key, not just top-level objects.
    • Fix: Anchor paths explicitly (e.g., $.store.book).
  9. Empty Results vs. null

    • Issue: evaluate() returns null for missing paths, while evaluateAll() returns an empty array. Since 8.1.9 a null may also be a legitimate value returned by a selector—indistinguishable from "not found" via evaluate() alone.
    • Fix: Use evaluateAll() + count() when presence matters; reserve evaluate() for single-value lookups.

Debugging Tips

  • 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 (extend these with the 8.1.9 behaviours):

    // 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]'));
    
    // Test null preservation in index/slice selectors (8.1.9)
    $this->assertSame([null, 'b'], $jsonPath->evaluateAll(['a' => null, 'b' => 'b'], '$[*]'));
    $this->assertSame([null, 'b'], $jsonPath->evaluateAll([null, 'b'], '$[0:2]'));
    
    // Test anchored match()
    $this->assertSame([], $jsonPath->evaluateAll(
        ['xabcx', 'abc'],
        '$[?match(@, "abc")]'
    ));
    
  • Bisect Upgrades with a Failing Path Corpus: Keep a fixture file of real production paths and assert their outputs; after bumping to 8.1.9 the diff will surface the anchoring/null-selection changes immediately.

Extension Points

  1. Custom JSONPath Functions

    • Issue: Need custom functions (e.g., contains()).
    • Workaround: Pre-process JSON with Laravel Collections or use a wrapper:
      $filtered = collect($json)->filter(fn($item) => str_contains($item, 'search'));
      
  2. Laravel Facade

    • Tip: Create a facade for cleaner syntax:
      // 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');
      
  3. Safe Path Builder Helper

    • Tip: Centralize escaping so no caller interpolates raw keys:
      class PathBuilder
      {
          public static function key(string $name): string
          {
              return '[' . JsonPath::key($name) . ']';
          }
      }
      // $jsonPath->evaluate('$.data' . PathBuilder::key($field), $payload);
      
  4. Performance Optimization

    • Tip: Cache parsed JSON for repeated queries:
      $cacheKey = md5($json . $path);
      return Cache::remember($cacheKey, now()->addMinutes(10), fn() =>
          JsonPath::search($json, $path)
      );
      
  5. Integration with Laravel HTTP Client

    • Tip: Add a macro to Laravel’s 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');
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc