ergebnis/json-pointer
RFC 6901 JSON Pointer abstraction for PHP. Create, parse, and encode reference tokens from plain strings, JSON strings, or URI fragment identifiers, with helpers to output JSON-safe and URI-safe forms. Install via Composer: ergebnis/json-pointer.
Installation:
composer require ergebnis/json-pointer
Add to composer.json under require if not using Composer directly.
First Use Case: Navigate to a nested JSON value using a pointer:
use Ergebnis\Json\Pointer;
$pointer = Pointer\JsonPointer::fromJsonString('/user/profile/address');
$data = json_decode('{"user": {"profile": {"address": "123 Main St"}}}', true);
$address = $pointer->resolve($data); // Returns "123 Main St"
Core Classes:
JsonPointer: Main class for constructing and manipulating pointers.ReferenceToken: Represents individual segments of a pointer (e.g., user, profile).Specification: For validating pointers against custom rules.Key Methods:
fromJsonString(), fromUriFragmentIdentifierString(): Create pointers from strings.toJsonString(), toUriFragmentIdentifierString(): Convert pointers to strings.append(): Extend a pointer with new segments.resolve(): Extract data from JSON using the pointer (not built-in; see Implementation Patterns).$pointer = Pointer\JsonPointer::fromJsonString('/users/0/name');
$pointer = Pointer\JsonPointer::fromUriFragmentIdentifierString('#/users/0/name');
$tokens = [
Pointer\ReferenceToken::fromString('users'),
Pointer\ReferenceToken::fromInt(0),
Pointer\ReferenceToken::fromString('name'),
];
$pointer = Pointer\JsonPointer::fromReferenceTokens(...$tokens);
$basePointer = Pointer\JsonPointer::fromJsonString('/users');
$userId = 1;
$pointer = $basePointer->append(Pointer\ReferenceToken::fromInt($userId));
function resolvePointer(Pointer\JsonPointer $pointer, array $data): mixed {
$current = $data;
foreach ($pointer->getReferenceTokens() as $token) {
$current = $current[$token->toString()] ?? null;
if ($current === null) break;
}
return $current;
}
$data = ['users' => [['name' => 'Alice']]];
$pointer = Pointer\JsonPointer::fromJsonString('/users/0/name');
$name = resolvePointer($pointer, $data); // 'Alice'
$spec = Pointer\Specification::closure(fn($p) => str_starts_with($p->toJsonString(), '/users'));
$spec->isSatisfiedBy($pointer); // true if pointer starts with '/users'
$spec = Pointer\Specification::anyOf(
Pointer\Specification::equals($pointer1),
Pointer\Specification::closure(fn($p) => $p->toJsonString() === '/users/1')
);
$uriFragment = $pointer->toUriFragmentIdentifierString(); // e.g., '#/users/0'
$url = "https://api.example.com/data{$uriFragment}";
Null Resolution:
resolve() (custom implementation) returns null. Handle this explicitly:
$value = resolvePointer($pointer, $data) ?? 'default';
URI Fragment Encoding:
#/path) require percent-encoding for special characters. Use toUriFragmentIdentifierString() instead of manual encoding:
// ❌ Avoid manual encoding (e.g., str_replace)
// ✅ Use:
$fragment = $pointer->toUriFragmentIdentifierString();
Integer Tokens:
/users/0) are treated as strings when resolved. Ensure your JSON uses numeric indices if needed:
$data = ['users' => [['name' => 'Alice']]]; // Correct
$data = ['users' => [{'0': ['name' => 'Alice']}]]; // Incorrect
Specification Logic:
Specification::closure() captures the closure’s scope. Avoid referencing non-serializable variables:
// ❌ Risky (closes over $userId)
$spec = Pointer\Specification::closure(fn($p) => $p->toJsonString() === "/users/{$userId}");
// ✅ Safer
$spec = Pointer\Specification::equals(Pointer\JsonPointer::fromJsonString("/users/{$userId}"));
Inspect Pointers:
toJsonString() or toString() to debug pointer structures:
dump($pointer->toJsonString()); // '/users/0/name'
Validate JSON:
json_last_error() if resolution fails:
$json = json_encode($data);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException('Invalid JSON');
}
Handle Edge Cases:
JsonPointer::document()) refer to the root of the JSON object./users/) are valid but may cause issues in resolution. Trim if necessary:
$pointerStr = trim($pointer->toJsonString(), '/');
Custom Resolvers:
resolve() logic for complex JSON (e.g., arrays of objects):
class CustomResolver {
public function resolve(Pointer\JsonPointer $pointer, array $data): mixed {
// Custom logic (e.g., handle wildcards, arrays)
}
}
Pointer Builders:
class PointerBuilder {
private $tokens = [];
public function segment(string $segment): self {
$this->tokens[] = Pointer\ReferenceToken::fromString($segment);
return $this;
}
public function build(): Pointer\JsonPointer {
return Pointer\JsonPointer::fromReferenceTokens(...$this->tokens);
}
}
$pointer = (new PointerBuilder())
->segment('users')
->segment('0')
->segment('name')
->build();
Specification Extensions:
Pointer\Specification::closure(fn($p) => preg_match('/^\/users\/\d+$/', $p->toJsonString()));
JsonPointer and ReferenceToken objects if the same pointer is used frequently.How can I help you explore Laravel packages today?