Expand description
This crate allows you to use JSONPath queries to extract nodelists from a serde_json::Value
.
The crate intends to adhere to the IETF JSONPath standard (RFC 9535). Check out the specification to read more about JSONPath query syntax and to find many examples of its usage.
§Features
This crate provides three key abstractions:
- The
JsonPath
struct, which represents a parsed JSONPath query. - The
NodeList
struct, which represents the result of a JSONPath query performed on aserde_json::Value
using theJsonPath::query
method. - The
LocatedNodeList
struct, which is similar toNodeList
, but includes the location of each node in the query string as aNormalizedPath
, and is produced by theJsonPath::query_located
method.
In addition, the JsonPathExt
trait is provided, which extends the serde_json::Value
type with the json_path
method for performing JSONPath queries.
Finally, the #[function]
attribute macro can be used to extend JSONPath
queries to use custom functions.
§Usage
§Parsing
JSONPath query strings can be parsed using the JsonPath
type:
use serde_json_path::JsonPath;
let path = JsonPath::parse("$.foo.bar")?;
You then have two options to query a serde_json::Value
using the parsed JSONPath:
JsonPath::query
or JsonPath::query_located
. The former will produce a NodeList
,
while the latter will produce a LocatedNodeList
. The two options provide similar
functionality, but it is recommended to use the former unless you have need of node locations
in the query results.
§Querying for single nodes
For queries that are expected to return a single node, use either the
exactly_one
or the at_most_one
method.
use serde_json::json;
let value = json!({ "foo": { "bar": ["baz", 42] } });
let path = JsonPath::parse("$.foo.bar[0]")?;
let node = path.query(&value).exactly_one()?;
assert_eq!(node, "baz");
JSONPath allows access via reverse indices:
let value = json!([1, 2, 3, 4, 5]);
let path = JsonPath::parse("$[-1]")?;
let node = path.query(&value).at_most_one()?;
assert_eq!(node, Some(&json!(5)));
Keep in mind, that for simple queries, the serde_json::Value::pointer
method may suffice.
§Querying for multiple nodes
For queries that are expected to return zero or many nodes, use the all
method. There are several selectors in JSONPath whose combination can produce
useful and powerful queries.
§Wildcards (*
)
Wildcards select everything under a current node. They work on both arrays, by selecting all array elements, and on objects, by selecting all object key values:
let value = json!({ "foo": { "bar": ["baz", "bop"] } });
let path = JsonPath::parse("$.foo.bar[*]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec!["baz", "bop"]);
§Slice selectors (start:end:step
)
Extract slices from JSON arrays using optional start
, end
, and step
values. Reverse
indices can be used for start
and end
, and a negative step
can be used to traverse
the array in reverse order. Consider the following JSON object, and subsequent examples:
let value = json!({ "foo": [1, 2, 3, 4, 5] });
start
, end
, and step
are all optional:
let path = JsonPath::parse("$.foo[:]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![1, 2, 3, 4, 5]);
Omitting end
will go to end of slice:
let path = JsonPath::parse("$.foo[2:]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![3, 4, 5]);
Omitting start
will start from beginning of slice:
let path = JsonPath::parse("$.foo[:2]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![1, 2]);
You can specify the step
size:
let path = JsonPath::parse("$.foo[::2]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![1, 3, 5]);
Or use a negative step
to go in reverse:
let path = JsonPath::parse("$.foo[::-1]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![5, 4, 3, 2, 1]);
Finally, reverse indices can be used for start
or end
:
let path = JsonPath::parse("$.foo[-2:]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![4, 5]);
§Filter expressions (?
)
Filter selectors allow you to use logical expressions to evaluate which
members in a JSON object or array will be selected. You can use the boolean &&
and ||
operators as well as parentheses to group logical expressions in your filters. The current node
(@
) operator allows you to utilize the node being filtered in your filter logic:
let value = json!({ "foo": [1, 2, 3, 4, 5] });
let path = JsonPath::parse("$.foo[?@ > 2 && @ < 5]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![3, 4]);
You can form relative paths on the current node, as well as absolute paths on the root ($
)
node when writing filters:
let value = json!({
"threshold": 40,
"readings": [
{ "val": 35, "msg": "foo" },
{ "val": 40, "msg": "bar" },
{ "val": 42, "msg": "biz" },
{ "val": 48, "msg": "bop" },
]
});
let path = JsonPath::parse("$.readings[? @.val > $.threshold ].msg")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec!["biz", "bop"]);
Filters also allow you to make use of functions in your queries:
let value = json!([
"a short string",
"a longer string",
"an unnecessarily long string",
]);
let path = JsonPath::parse("$[? length(@) < 20 ]")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec!["a short string", "a longer string"]);
§Descendant Operator (..
)
JSONPath query segments following a descendant operator (..
) will visit the input node and each of its descendants.
let value = json!({
"foo": {
"bar": {
"baz": 1
},
"baz": 2
},
"baz": 3,
});
let path = JsonPath::parse("$.foo..baz")?;
let nodes = path.query(&value).all();
assert_eq!(nodes, vec![2, 1]);
§Node locations and NormalizedPath
Should you need to know the locations of the nodes produced by your queries, you can make use
of the JsonPath::query_located
method to perform the query. The resulting
LocatedNodeList
contains both the nodes produced by the query, as well as their locations
represented by their NormalizedPath
.
let value = json!({
"foo": {
"bar": {
"baz": 1
},
"baz": 2
},
"baz": 3,
});
let path = JsonPath::parse("$..[? @.baz == 1]")?;
let location = path
.query_located(&value)
.exactly_one()?
.location()
.to_string();
assert_eq!(location, "$['foo']['bar']");
§Feature Flags
The following feature flags are supported:
tracing
- Enable internal tracing via tracingfunctions
- Enable user-defined functionsregex
- Enable thematch
andsearch
functions
Modules§
- Function Extensions in JSONPath
Structs§
- Error produced when expecting no more than one node from a query
- A parsed JSON Path query string
- A node within a JSON value, along with its location
- A list of nodes resulting from a JSONPath query, along with their locations
- An iterator over the locations in a
LocatedNodeList
- A list of nodes resulting from a JSONPath query
- An iterator over the nodes in a
LocatedNodeList
- Represents a Normalized Path from the JSONPath specification
- Error type for JSONPath query string parsing errors
Enums§
- Error produced when expecting exactly one node from a query
- An element within a
NormalizedPath
Traits§
- Extension trait that allows for JSONPath queries directly on
serde_json::Value
Attribute Macros§
- Register a function for use in JSONPath queries