Class JsonPathEvaluator
Applies a JsonPathQuery to a JSON value (RFC 9535).
The evaluator reads the JSON value through a JsonModel. It does not copy the value.
The result is a nodelist: each Node has the caller's own node instance and its
Normalized Path.
For a well-formed JsonModel and non-null arguments, the evaluator never throws an
exception. It never calls a model method with a null argument, and it calls a method
that is specific to a kind only for a node of that kind.
An evaluator is immutable and safe for use by more than one thread. Keep one instance and use it again.
Evaluate a query
JsonPathQuery query = query("$..book[?@.isbn].author");
List<Node<JsonNode>> nodes = JsonPathEvaluator.standard()
.evaluate(query, root, JacksonJsonModel.INSTANCE)
.orElse(List.of());
Node<JsonNode> first = nodes.getFirst();
JsonNode value = first.value(); // the Jackson node "Herman Melville" of the tree
NormalizedPath path = first.path(); // $['store']['book'][2]['author']
The result nodes are in the order that RFC 9535 specifies. Array elements are in array order.
Object members are in the order of JsonModel.memberCursor(Object).
Filters and functions
// Comparisons, logical operators, and the standard functions.
List<String> queries = List.of(
"$.store.book[?@.price < 10 && @.category == 'fiction'].title", // ["Moby Dick"]
"$.store.book[?search(@.author, 'Re*s')].author", // ["Nigel Rees"]
"$.store.book[?match(@.title, '.*Ring.*')].price", // [22.99]
"$.store[?length(@.color) == 3].color", // ["red"]
"$.store.book[?count(@.*) == 5].title", // the books with an isbn
"$.store.book[-1:].title"); // ["The Lord of the Rings"]
for (String text : queries) {
List<Node<JsonNode>> nodes = evaluator.evaluate(query(text), root, JacksonJsonModel.INSTANCE).orElse(List.of());
System.out.println(text + " -> " + nodes.stream().map(Node::value).toList());
}
Errors
A query from the parser is valid, so for such a query only the overflow indications of
JsonPathEvaluator.Limits can occur. A query that a caller makes directly can also give a validity error:
// A query that is made directly, not by the parser, can be invalid.
JsonPathQuery bad = new JsonPathQuery(List.of(new ca.marcusdunn.jsonlens.path.core.query.Segment.Child(
List.of(new ca.marcusdunn.jsonlens.path.core.query.Selector.Index(1L << 60)))));
Result<List<Node<JsonNode>>, EvaluationError> result =
JsonPathEvaluator.standard().evaluate(bad, MAPPER.readTree("[]"), JacksonJsonModel.INSTANCE);
// result: Err(IntegerOutOfRange[value=1152921504606846976])
Function extensions
An evaluator from withFunctions(List) also has the read-only function extensions that you give.
It still accepts each JsonModel. See FunctionExtension.
An extension that makes new JSON values is a BuildingFunctionExtension. Register it with
withFunctions(List, List), which gives a BuildingEvaluator. A building evaluator accepts only a
model that also implements JsonFactory.
-
Nested Class Summary
Nested Classes -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intThe maximum number of nested expressions, filters, and function calls in a query. -
Method Summary
Modifier and TypeMethodDescription<N> Result<List<Node<N>>, EvaluationError> evaluate(JsonPathQuery query, N root, JsonModel<N> model) Applies a query to a JSON value.Returns the signatures of the functions that this evaluator has.limits()Returns the limits of this evaluator.static JsonPathEvaluatorstandard()Returns an evaluator with the standard functions (length, count, match, search, and value) and the default limits.static Result<JsonPathEvaluator, ExtensionError> withFunctions(List<FunctionExtension> extensions) Returns an evaluator with the standard functions and more function extensions.static Result<BuildingEvaluator, ExtensionError> withFunctions(List<FunctionExtension> readOnly, List<BuildingFunctionExtension> building) Returns an evaluator with the standard functions, read-only function extensions, and building function extensions.withLimits(JsonPathEvaluator.Limits limits) Returns an evaluator with the same functions and other limits.
-
Field Details
-
MAX_QUERY_DEPTH
public static final int MAX_QUERY_DEPTHThe maximum number of nested expressions, filters, and function calls in a query. A query from the parser is always below this limit. A deeper query givesEvaluationError.QueryTooDeep.- See Also:
-
-
Method Details
-
standard
Returns an evaluator with the standard functions (length, count, match, search, and value) and the default limits.- Returns:
- an evaluator with the standard functions
-
withFunctions
public static Result<JsonPathEvaluator, ExtensionError> withFunctions(List<FunctionExtension> extensions) Returns an evaluator with the standard functions and more function extensions.
The parser that makes the queries must know the same signatures:
// The parser needs the signature, to check that queries are well-typed. Result<JsonPathParser, FunctionRegistrationError> parser = JsonPathParser.withFunctions(List.of(First.SIGNATURE)); // The evaluator needs the implementation. Result<JsonPathEvaluator, ExtensionError> evaluator = JsonPathEvaluator.withFunctions(List.of(new First())); JsonPathQuery query = parser.orElse(JsonPathParser.standard()) .parse("$[?first(@.*) > 4]") .orElse(null); List<Node<JsonNode>> nodes = evaluator.orElse(JsonPathEvaluator.standard()) .evaluate(query, root, JacksonJsonModel.INSTANCE) .orElse(List.of()); // nodes: the path $[1]- Parameters:
extensions- the function extensions- Returns:
- the evaluator, or an error if a name is not valid or is used more than once, or if a read-only extension has a ValueType parameter
-
withFunctions
public static Result<BuildingEvaluator, ExtensionError> withFunctions(List<FunctionExtension> readOnly, List<BuildingFunctionExtension> building) Returns an evaluator with the standard functions, read-only function extensions, and building function extensions.
A building extension can make new JSON values, so the evaluator accepts only models that also implement
JsonFactory. SeeBuildingFunctionExtension.- Parameters:
readOnly- the read-only function extensionsbuilding- the building function extensions- Returns:
- the evaluator, or an error if a name is not valid or is used more than once, or if a read-only extension has a ValueType parameter
-
withLimits
Returns an evaluator with the same functions and other limits.- Parameters:
limits- the limits- Returns:
- the evaluator
-
limits
-
functions
Returns the signatures of the functions that this evaluator has.- Returns:
- the signatures, the standard functions first
-
evaluate
public <N> Result<List<Node<N>>, EvaluationError> evaluate(JsonPathQuery query, N root, JsonModel<N> model) Applies a query to a JSON value.
The evaluator first checks the validity rules that the Java types of a query cannot express: the I-JSON range of integers, the signatures of function calls, and the nesting depth. A query from the parser always obeys these rules. Then the evaluator applies the segments of the query, one after the other, to the root node.
JsonPathQuery query = query("$..book[?@.isbn].author"); List<Node<JsonNode>> nodes = JsonPathEvaluator.standard() .evaluate(query, root, JacksonJsonModel.INSTANCE) .orElse(List.of()); Node<JsonNode> first = nodes.getFirst(); JsonNode value = first.value(); // the Jackson node "Herman Melville" of the tree NormalizedPath path = first.path(); // $['store']['book'][2]['author']Result When Okwith an empty listthe query selects nothing. This is not an error. Okwith nodesthe query selects nodes ErrwithEvaluationError.NodelistTooLargeorEvaluationError.RegexTooComplexa limit of JsonPathEvaluator.Limitsis passedErrwith anotherEvaluationErrorthe query is not valid, or a function extension does not obey its signature - Type Parameters:
N- the node type- Parameters:
query- the queryroot- the query argument: the root node valuemodel- the model for the node type- Returns:
- the nodelist, or an error
-