Package ca.marcusdunn.jsonlens.path.evaluator


@NullMarked package ca.marcusdunn.jsonlens.path.evaluator

The RFC 9535 JSONPath evaluator: applies a query to a JSON value through a JsonModel.

Quick start

// 1. Read the JSON value with your own JSON library. Keep numbers exact.
JsonMapper mapper = JsonMapper.builder()
        .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
        .build();
JsonNode root = mapper.readTree(BOOKSTORE);

// 2. Parse the query. A query that is not valid gives an error value.
Result<JsonPathQuery, ParseError> parsed =
        JsonPathParser.standard().parse("$.store.book[?@.price < 10].title");

// 3. Apply the query to the value through a JsonModel.
switch (parsed) {
    case Result.Ok(JsonPathQuery query) -> {
        Result<List<Node<JsonNode>>, EvaluationError> result =
                JsonPathEvaluator.standard().evaluate(query, root, JacksonJsonModel.INSTANCE);
        switch (result) {
            case Result.Ok(List<Node<JsonNode>> nodes) -> {
                for (Node<JsonNode> node : nodes) {
                    lines.add(node.path() + " = " + node.value());
                }
            }
            case Result.Err(EvaluationError error) -> lines.add("Overflow: " + error.message());
        }
    }
    case Result.Err(ParseError error) -> lines.add("Bad query: " + error.message());
}
// lines:
//   $['store']['book'][0]['title'] = "Sayings of the Century"
//   $['store']['book'][2]['title'] = "Moby Dick"

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());
}

Types

Type Purpose
JsonPathEvaluator applies a query to a JSON value
Node a result node: the value and its Normalized Path
EvaluationError the reason why the evaluator cannot give a correct result
JsonPathEvaluator.Limits the resource limits of an evaluation
FunctionExtension a read-only function extension: it runs with each model
BuildingFunctionExtension a function extension that can make new values
BuildingEvaluator an evaluator with building extensions: it needs a model that can build nodes
Instance the arguments and results of functions: nodes, logical values, and nodelists
ExtensionError the reason why a set of function extensions is not valid