Skip to main content

Quick start

This page gets you started using JSONPath, JSON Pointer and JSON Patch with JavaScript. See JSONPath Syntax for an introduction to JSONPath syntax.

JSONPath

Find all values matching a JSONPath query with jsonpath.query(). It takes a string (the query) and some data to apply the query to. It always returns an instance of JSONPathNodeList. Use JSONPathNodeList.values() to get an array of values matching the query.

import { jsonpath } from "json-p3";

const data = {
users: [
{ name: "Sue", score: 100 },
{ name: "John", score: 86 },
{ name: "Sally", score: 84 },
{ name: "Jane", score: 55 },
],
};

const nodes = jsonpath.query("$.users[?@.score < 100].name", data);
console.log(nodes.values()); // [ 'John', 'Sally', 'Jane' ]

query() is re-exported to JSON P3's top-level namespace, so you could do the following instead.

import { query } from "json-p3";

const data = {
users: [
{ name: "Sue", score: 100 },
{ name: "John", score: 86 },
{ name: "Sally", score: 84 },
{ name: "Jane", score: 55 },
],
};

const nodes = query("$.users[?@.score < 100].name", data);
console.log(nodes.values()); // [ 'John', 'Sally', 'Jane' ]

A JSONPathNodeList is a list of JSONPathNode objects, one for each value in the target document matching the query. Each node has a:

  • value - The value found in the target JSON document. This could be an array, object or primitive value.
  • location - An array of property names and array indices that were required to reach the node's value in the target JSON document.
  • path - The normalized JSONPath to this node in the target JSON document.

Normalized paths

A normalized path is one that uniquely identifies the node's value within the target document. Use JSONPathNodeList.paths() to retrieve all paths from a node list.

// .. continued from above
console.log(nodes.paths());
output
[
"$['users']['1']['name']",
"$['users']['2']['name']",
"$['users']['3']['name']"
]

Locations

Use JSONPathNodeList.locations() to get an array of locations for each node in the list.

// .. continued from above
console.log(nodes.locations());
output
[
[ 'users', 1, 'name' ],
[ 'users', 2, 'name' ],
[ 'users', 3, 'name' ]
]

Iterating node lists

Node lists are iterable too.

// .. continued from above
for (const node of nodes) {
console.log(`${node.value} @ ${node.path}`);
}
output
John @ $['users'][1]['name']
Sally @ $['users'][2]['name']
Jane @ $['users'][3]['name']

Lazy queries

lazyQuery() is an alternative to query(). lazyQuery() can be faster and more memory efficient if querying large datasets, especially when using recursive descent selectors. Conversely, query() is usually the better choice when working with small datasets.

lazyQuery() returns an iterable sequence of JSONPathNode objects which is not a JSONPathNodeList.

import { lazyQuery } from "json-p3";

const data = {
users: [
{ name: "Sue", score: 100 },
{ name: "John", score: 86 },
{ name: "Sally", score: 84 },
{ name: "Jane", score: 55 },
],
};

for (const node of lazyQuery("$.users[?@.score < 100].name", data)) {
console.log(node.value);
}

// John
// Sally
// Jane

Compilation

query() is a convenience function equivalent to new JSONPathEnvironment().compile(path).query(data). Use jsonpath.compile() to construct a JSONPath object that can be applied to different data repeatedly.

import { jsonpath } from "json-p3";

const data = {
users: [
{ name: "Sue", score: 100 },
{ name: "John", score: 86 },
{ name: "Sally", score: 84 },
{ name: "Jane", score: 55 },
],
};

const path = jsonpath.compile("$.users[?@.score < 100].name");
const nodes = path.query(data);
console.log(nodes.values()); // [ 'John', 'Sally', 'Jane' ]

compile() is also re-exported to JSON P3's top-level namespace.

JSON Pointer

Resolve a JSON Pointer (RFC 6901) against some data using jsonpointer.resolve().

import { jsonpointer } from "json-p3";

const data = {
users: [
{ name: "Sue", score: 100 },
{ name: "John", score: 86 },
{ name: "Sally", score: 84 },
{ name: "Jane", score: 55 },
],
};

const rv = jsonpointer.resolve("/users/1", data);
console.log(rv); // { name: 'John', score: 86 }

resolve() is a convenience function equivalent to new JSONPointer(pointer).resolve(data). Use the JSONPointer constructor when you need to resolve the same pointer repeatedly against different data.

import { JSONPointer } from "json-p3";

const someData = {
users: [
{ name: "Sue", score: 100 },
{ name: "John", score: 86 },
{ name: "Sally", score: 84 },
],
};

const otherData = {
users: [{ name: "Brian" }, { name: "Roy" }],
};

const pointer = new JSONPointer("/users/1");
console.log(pointer.resolve(someData)); // { name: 'John', score: 86 }
console.log(pointer.resolve(otherData)); // { name: 'Roy' }

Errors and fallbacks

If the pointer can't be resolved against the argument JSON value, one of JSONPointerIndexError, JSONPointerKeyError or JSONPointerTypeError is thrown. All three exceptions inherit from JSONPointerResolutionError.

// .. continued from above
const rv = pointer.resolve("/users/1/age", data);
// JSONPointerKeyError: no such property ("/users/1/age")

A fallback value can be given as a third argument, which will be returned in the event of a JSONPointerResolutionError.

// .. continued from above
const rv = pointer.resolve("/users/1/age", data, -1);
console.log(rv); // -1

Relative JSON Pointers

We support Relative JSON Pointers via the to(rel) method of JSONPointer, where rel is a relative JSON pointer string, and a new JSONPointer is returned.

import { JSONPointer } from "json-p3";

const data = { foo: { bar: [1, 2, 3], baz: [4, 5, 6] } };
const pointer = new JSONPointer("/foo/bar/2");

console.log(pointer.resolve(data)); // 3
console.log(pointer.to("0-1").resolve(data)); // 2
console.log(pointer.to("2/baz/2").resolve(data)); // 6

JSON Patch

Apply a JSON Patch (RFC 6902) to some data with jsonpatch.apply(). Data is modified in place..

import { jsonpatch } from "json-p3";

const ops = [
{ op: "add", path: "/some/foo", value: { foo: {} } },
{ op: "add", path: "/some/foo", value: { bar: [] } },
{ op: "copy", from: "/some/other", path: "/some/foo/else" },
{ op: "add", path: "/some/foo/bar/-", value: 1 },
];

const data = { some: { other: "thing" } };
jsonpatch.apply(ops, data);
console.log(data);
// { some: { other: 'thing', foo: { bar: [Array], else: 'thing' } } }

apply() is also re-exported from JSON P3's top-level namespace.

JSONPatch constructor

jsonpatch.apply() is a convenience function equivalent to new JSONPatch(ops).apply(data). Use the JSONPatch constructor when you need to apply the same patch to multiple different data structures.

import { JSONPatch } from "json-p3";

const patch = new JSONPatch([
{ op: "add", path: "/some/foo", value: { foo: {} } },
{ op: "add", path: "/some/foo", value: { bar: [] } },
{ op: "copy", from: "/some/other", path: "/some/foo/else" },
{ op: "add", path: "/some/foo/bar/-", value: 1 },
]);

const data = { some: { other: "thing" } };
patch.apply(data);
console.log(data);
// { some: { other: 'thing', foo: { bar: [Array], else: 'thing' } } }

Builder API

JSONPatch objects offer a builder interface for constructing JSON patch documents. We use strings as JSON Pointers in this example, but existing JSONPointer objects are OK too.

import { JSONPatch } from "json-p3";

const data = { some: { other: "thing" } };

const patch = new JSONPatch()
.add("/some/foo", { foo: [] })
.add("/some/foo", { bar: [] })
.copy("/some/other", "/some/foo/else")
.copy("/some/foo/else", "/some/foo/bar/-");

patch.apply(data);
console.log(JSON.stringify(data, undefined, " "));
output
{
"some": {
"other": "thing",
"foo": {
"bar": ["thing"],
"else": "thing"
}
}
}