Quick Start
This page gets you started using JSONPath, JSON Pointer and JSON Patch wih Python. See JSONPath Syntax for information on JSONPath selector syntax.
findall(path, data)
Find all values matching a JSONPath query using jsonpath.findall().
This function takes two arguments:
path: a JSONPath query as a string (e.g."$.users[*].name")data: the JSON document to query
It always returns a list of matched values, even if the path resolves to a single result or nothing at all.
The data argument can be:
- A Python
Mapping(e.g.dict) orSequence(e.g.list) - A JSON-formatted string
- A file-like object containing JSON
For example, the following query extracts all user names from a dictionary containing a list of user objects:
import jsonpath
data = {
"users": [
{
"name": "Sue",
"score": 100,
},
{
"name": "John",
"score": 86,
},
{
"name": "Sally",
"score": 84,
},
{
"name": "Jane",
"score": 55,
},
]
}
user_names = jsonpath.findall("$.users.*.name", data)
Where user_names is now equal to:
If the same data were in a file called users.json, we might use findall() like this:
finditer(path, data)
Use jsonpath.finditer() to iterate over instances of jsonpath.JSONPathMatch for every object in data that matches path. It accepts the same arguments as findall(), a query string and data from which to select matches.
import jsonpath
data = {
"users": [
{
"name": "Sue",
"score": 100,
},
{
"name": "John",
"score": 86,
},
{
"name": "Sally",
"score": 84,
},
{
"name": "Jane",
"score": 55,
},
]
}
matches = jsonpath.finditer("$.users.*.name", data)
for match in matches:
print(matches)
The string representation of a JSONPathMatch shows the matched object and the canonical path to that object.
'Sue' @ $['users'][0]['name']
'John' @ $['users'][1]['name']
'Sally' @ $['users'][2]['name']
'Jane' @ $['users'][3]['name']
The selected object is available from a JSONPathMatch as obj and its path, as a string, as path. Other useful properties of JSONPathMatch include a reference to the parent match, a list of child matches, and a parts tuple of keys and indices that make up the path.
compile(path)
When you have a JSONPath query that needs to be matched against different data repeatedly, you can compile the path ahead of time using jsonpath.compile(). It takes a query as a string and returns an instance of JSONPath. JSONPath has findall() and finditer() methods that behave similarly to package-level findall() and finditer(), just without the path argument.
import jsonpath
some_data = {
"users": [
{
"name": "Sue",
"score": 100,
},
{
"name": "John",
"score": 86,
},
]
}
other_data = {
"users": [
{
"name": "Sally",
"score": 84,
},
{
"name": "Jane",
"score": 55,
},
]
}
path = jsonpath.compile("$.users.*.name")
some_users = path.findall(some_data)
other_users = path.findall(other_data)
match(path, data)
New in version 0.8.0
Get a jsonpath.JSONPathMatch instance for the first match found in data. If there are no matches, None is returned. match() accepts the same arguments as findall().
import jsonpath
data = {
"users": [
{
"name": "Sue",
"score": 100,
},
{
"name": "John",
"score": 86,
},
{
"name": "Sally",
"score": 84,
},
{
"name": "Jane",
"score": 55,
},
]
}
match = jsonpath.match("$.users[?@.score > 85].name", data)
if match:
print(match) # 'Sue' @ $['users'][0]['name']
print(match.obj) # Sue
pointer.resolve(pointer, data)
New in version 0.8.0
Resolves a JSON Pointer (RFC 6901) against a JSON document, returning the value located at the specified path.
The pointer argument can be either:
- A string representation of a JSON Pointer (e.g.,
"/foo/bar/0") - A list of unescaped pointer segments (e.g.,
["foo", "bar", "0"])
The data argument can be:
- A Python data structure (
dict,list, etc.) - A JSON-formatted string
- A file-like object containing JSON
from jsonpath import pointer
data = {
"users": [
{
"name": "Sue",
"score": 100,
},
{
"name": "John",
"score": 86,
},
{
"name": "Sally",
"score": 84,
},
{
"name": "Jane",
"score": 55,
},
]
}
sue_score = pointer.resolve("/users/0/score", data)
print(sue_score) # 100
jane_score = pointer.resolve(["users", 3, "score"], data)
print(jane_score) # 55
If the pointer cannot be resolved against the target JSON data — due to a missing key, an out-of-range index, or an unexpected data type — an exception will be raised:
JSONPointerKeyError– when a referenced key is missing from an objectJSONPointerIndexError– when an array index is out of boundsJSONPointerTypeError– when a path segment expects the wrong type (e.g., indexing into a non-array)
All of these exceptions are subclasses of JSONPointerResolutionError.
You can optionally provide a default value to resolve(), which will be returned instead of raising an error if the pointer cannot be resolved.
from jsonpath import pointer
data = {
"users": [
{
"name": "Sue",
"score": 100,
},
{
"name": "John",
"score": 86,
},
]
}
sue_score = pointer.resolve("/users/99/score", data, default=0)
print(sue_score) # 0
See also JSONPathMatch.pointer(), which builds a JSONPointer from a JSONPathMatch.
patch.apply(patch, data)
New in version 0.8.0
Apply a JSON Patch (RFC 6902) to some data. A JSON Patch defines update operation to perform on a JSON document.
patch can be a string or file-like object containing a valid JSON Patch document, or an iterable of dictionaries.
data is the target JSON document to modify. If data is a string or file-like object, it will be loaded with json.loads. Otherwise data should be a JSON-like data structure and will be modified in place.
Warning
Data passed to patch.apply() and JSONPatch.apply() is modified in place, even if a patch operation fails. If you need to preserve input data on failure, you should make a copy of your data before calling apply(), or use JSONPatch.atomic(), which performs a deep copy, then mutates input data on success.
from jsonpath import patch
patch_operations = [
{"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},
]
data = {"some": {"other": "thing"}}
patch.apply(patch_operations, data)
print(data) # {'some': {'other': 'thing', 'foo': {'bar': [1], 'else': 'thing'}}}
Use the JSONPatch class to create a patch for repeated application.
from jsonpath import JSONPatch
patch = 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},
]
)
data = {"some": {"other": "thing"}}
patch.apply(data)
print(data) # {'some': {'other': 'thing', 'foo': {'bar': [1], 'else': 'thing'}}}
JSONPatch also offers a builder API for constructing JSON patch documents. We use strings as JSON Pointers in this example, but existing JSONPointer instances are OK too.
from jsonpath import JSONPatch
patch = (
JSONPatch()
.add("/some/foo", {"foo": []})
.add("/some/foo", {"bar": []})
.copy("/some/other", "/some/foo/else")
.add("/some/foo/bar/-", "/some/foo/else")
)
data = {"some": {"other": "thing"}}
patch.apply(data)
print(data) # {'some': {'other': 'thing', 'foo': {'bar': [1], 'else': 'thing'}}}
patch.atomic(patch, data)
New in version 2.1.0
Data passed to patch.apply() and JSONPatch.apply() is modified in place, even if a patch operation fails. If you need to preserve input data on failure, you should make a copy of your data before calling apply(), or use patch.atomic(), which performs a deep copy, then mutates input data on success.
Note
patch.atomic() and JSONPatch.atomic() are limited to JSON-like like dictionaries and lists, whereas patch.apply() and JSONPatch.apply(data) accept file-like objects, JSON-formatted strings or JSON-like data.
import copy
from typing import Any
from jsonpath import JSONPatch
from jsonpath import JSONPatchError
patch = JSONPatch(
[
{"op": "replace", "path": "/a/b/c", "value": 42},
{"op": "test", "path": "/a/b/c", "value": "C"}, # Always fails
]
)
data: dict[str, Any] = {"a": {"b": {"c": 1}}}
data_ = copy.deepcopy(data)
try:
patch.atomic(data)
except JSONPatchError:
# TODO: something
pass
assert data == data_
patch.patched(patch, data)
New in version 2.2.0
patch.patched(ops, data) and JSONPatch.patched(data) never mutate data. They apply patch operations to a deep copy of data and return the patched copy.
from jsonpath import patch
patch_operations = [
{"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},
]
data = {"some": {"other": "thing"}}
patched_data = patch.patched(patch_operations, data)
assert data != patched_data
What's Next?
Read about the Query Iterators API or user-defined filter functions. Also see how to make extra data available to filters with Extra Filter Context.
findall(), finditer() and compile() are shortcuts that use the defaultJSONPathEnvironment. jsonpath.findall(path, data) is equivalent to:
If you would like to customize Python JSONPath, see Advanced Usage.