SearchCtrlK

  • ↑↓navigate
  • ↵open
  • escclose

Can’t find it? Contact us

@sdxc/jsonpath

RFC 9535 JSONPath queries over JSON values, with normalized paths and every function extension

Terminal
npm install @sdxc/jsonpath
Terminal
pnpm add @sdxc/jsonpath
Terminal
yarn add @sdxc/jsonpath
Terminal
bun add @sdxc/jsonpath
Depends on
remix

RFC 9535 JSONPath queries over JSON values, with normalized paths and every function extension.

JSONPath selects nodes from a JSON value with expressions like $.store.book[?@.price < 10].title. This package implements the whole standard: name, wildcard, index, slice and filter selectors; child and descendant segments; comparisons and logical operators; and the length, count, match, search and value functions, with match and search reading I-Regexp patterns. It passes the official JSONPath compliance test suite.

Installation

Terminal
npm add @sdxc/jsonpath

Fallible calls return @sdxc/result values.

Usage

Run a query once

typescript
import { query } from "@sdxc/jsonpath";

query({ users: [{ name: "Ada", admin: true }, { name: "Alan" }] }, "$.users[?@.admin].name");
// success([{ value: "Ada", location: ["users", 0, "name"], path: "$['users'][0]['name']" }])

Parse once, select many times

typescript
import { JSONPath } from "@sdxc/jsonpath";
import { unwrap } from "@sdxc/result";

let cheap = unwrap(JSONPath.parse("$..book[?@.price < 10].title"));

cheap.values(store); // ["Sayings of the Century", "Moby Dick"]
cheap.values(otherStore);

Functions

typescript
import { JSONPath } from "@sdxc/jsonpath";
import { unwrap } from "@sdxc/result";

unwrap(JSONPath.parse("$.books[?length(@.title) > 15]"));
unwrap(JSONPath.parse("$.books[?count(@.authors[*]) >= 2]"));
unwrap(JSONPath.parse("$.users[?match(@.email, '[a-z]+@example\\.com')]"));
unwrap(JSONPath.parse("$.books[?search(@.title, 'Rings')]"));
unwrap(JSONPath.parse("$.groups[?value(@..id) == 7]"));

In match and search patterns, . matches anything but a line break, and ^ and $ anchor as the compliance suite expects. A pattern that is not an I-Regexp makes the function false.

Command line

Terminal
bunx @sdxc/jsonpath query '$.store.book[?@.price < 10].title' store.json

With @sdxc/kit installed, sdxc pkg jsonpath … runs the same command line, using the version your project installs.

query prints the matched values as a JSON array, reading the document from a file or from stdin. --paths prints normalized paths instead, --first prints the first match alone, and --fail-empty exits 1 when nothing matches. An invalid expression exits 2 and points at the offset where it fails. bunx @sdxc/jsonpath --help lists every flag.

Terminal
cat package.json | bunx @sdxc/jsonpath query '$.name' --first

API

JSONPath.parse(source: string): Result<JSONPath, JsonPathSyntaxError>

Parses a query, refusing everything RFC 9535 refuses: invalid syntax, whitespace before or after the query, an index outside ±(2^53 − 1), an unknown function, and a function argument of the wrong type, such as length(@.*) or comparing a query that can select several nodes.

jsonPath.select(document: JSONValue): JsonPathNode[]

The selected nodes, in the order RFC 9535 defines. Object members are visited in the order the object holds them.

jsonPath.values(document: JSONValue): JSONValue[]

The selected values alone.

jsonPath.source

The query text.

query(document: JSONValue, source: string): Result<JsonPathNode[], JsonPathSyntaxError>

JSONPath.parse and select in one call.

normalizedPath(location: readonly (string | number)[]): string

The normalized path of a location, the canonical query for one node: ["a", 0] becomes $['a'][0], with ', \ and control characters escaped.

JsonPathSyntaxError

The parse failure; offset is where in the query the problem was found.

Types

JsonPathNode

{ value, location, path }: the selected value, its member names and indexes from the root, and its normalized path.

JSONValue

The JSON values a query reads.

Pattern: Patch Every Matching Node

location is the node's path as segments, which @sdxc/json-pointer turns into the pointers @sdxc/json-patch operations aim at.

typescript
import { apply } from "@sdxc/json-patch";
import { stringify } from "@sdxc/json-pointer";
import { JSONPath } from "@sdxc/jsonpath";
import { unwrap } from "@sdxc/result";

let overdue = unwrap(JSONPath.parse("$.invoices[?@.due < '2026-01-01' && !@.paid].status"));

let patch = overdue.select(ledger).map((node) => ({
	op: "replace" as const,
	path: stringify(node.location),
	value: "overdue",
}));

let next = apply(ledger, patch);

Pattern: Let Clients Filter A Collection

A query string carries a JSONPath filter; the handler parses it once and answers 400 with the offset when it is invalid.

typescript
import { JSONPath } from "@sdxc/jsonpath";
import { isFailure } from "@sdxc/result";

async function listOrders(request: Request) {
	let filter = new URL(request.url).searchParams.get("filter") ?? "$[*]";
	let parsed = JSONPath.parse(filter);
	if (isFailure(parsed)) {
		return Response.json(
			{ error: parsed.error.message, offset: parsed.error.offset },
			{ status: 400 },
		);
	}
	return Response.json(parsed.data.values(await orders.all()));
}
API

Can’t find it? Contact us