Package ca.marcusdunn.jsonlens.path.core.query


@NullMarked package ca.marcusdunn.jsonlens.path.core.query

The abstract syntax tree of a JSONPath query (RFC 9535).

The parser makes these trees from query text. A caller can also make them directly, and JsonPathQuery.toString() writes a tree as query text.

Grammar and types

The types follow the ABNF grammar of RFC 9535. Where the grammar or the type system of Section 2.4 restricts an expression, the Java types restrict it too. Thus many queries that are not valid cannot be made at all.

Grammar Type
jsonpath-query JsonPathQuery: $ and zero or more Segments
child-segment, descendant-segment Segment.Child, Segment.Descendant
name-selector, wildcard-selector, index-selector, slice-selector, filter-selector Selector.Name, Selector.Wildcard, Selector.Index, Selector.Slice, Selector.Filter
logical-expr LogicalExpression
comparable ComparableExpression: a Literal, a SingularQuery, or a FunctionCall.Value
filter-query FilterQuery
function-expr FunctionCall: FunctionCall.Value, FunctionCall.Logical, or FunctionCall.Nodes
function-argument FunctionArgument: one record for each declared parameter type

For example, a side of a LogicalExpression.Comparison is a ComparableExpression. A query that can select more than one node is not a ComparableExpression, so it cannot be a side of a comparison.

Make a query directly

// $.store.book[?length(@.title) > 10]['title'], made without the parser.
LogicalExpression longTitle = new LogicalExpression.Comparison(
        new FunctionCall.Value("length", List.of(new FunctionArgument.Value(
                new SingularQuery(Identifier.CURRENT, List.of(new SingularSegment.Name("title")))))),
        ComparisonOperator.GREATER,
        new Literal.NumberLiteral(BigDecimal.TEN));
JsonPathQuery query = new JsonPathQuery(List.of(
        new Segment.Child(List.of(new Selector.Name("store"))),
        new Segment.Child(List.of(new Selector.Name("book"))),
        new Segment.Child(List.of(new Selector.Filter(longTitle))),
        new Segment.Child(List.of(new Selector.Name("title")))));

String text = query.toString(); // $['store']['book'][?length(@['title']) > 10]['title']

The Java types cannot express all validity rules. For example, an index must be in the I-JSON range, and a function call must agree with the signature of the function. The evaluator checks these rules before it applies a query.

Safe query construction

JsonPathQuery.toString() escapes all names and strings. Thus a name from user input cannot change the structure of a query (RFC 9535, Section 4.2):

// A name from user input cannot change the structure of the query.
String input = "x'] || $..*[?@ == '";
JsonPathQuery query = new JsonPathQuery(List.of(new Segment.Child(List.of(new Selector.Name(input)))));
String text = query.toString(); // $['x\'] || $..*[?@ == \'']