json_path

This library provides predicates for parsing, generating, and evaluating JSONPath queries for native Logtalk JSON terms.

It is based on RFC 9535:

Current implementation status

This implementation currently supports:

  • child segments

  • descendant segments

  • name selectors

  • wildcard selectors

  • index selectors, including negative indexes

  • slice selectors

  • filter selectors

  • existence tests

  • logical operators: ||, &&, and !

  • comparisons using literals, singular queries, and supported function expressions

  • function expressions: length(), count(), match(), search(), and value()

  • local I-Regexp matching support for alternation, grouping, wildcard ., character classes, single-character escapes, and quantifiers (*, +, ?, {m}, {m,}, {m,n})

  • Unicode general-category escapes \p{...} and \P{...} using backend unicode_property/2 support when available

  • additional local validation for malformed character classes and for unsafe explicit range quantifiers (nested explicit ranges and very large upper bounds/spans are rejected)

  • validation of index and slice integers against the RFC 9535 I-JSON exact-integer range

  • validation that queries supplied in codes(...) representation contain only Unicode scalar values

  • normalized path reporting

Still pending:

  • the remaining RFC 9535 function-extension surface

API documentation

Open the ../../apis/library_index.html#json_path link in a web browser.

Loading

To load all entities in this library, load the loader.lgt file:

| ?- logtalk_load(json_path(loader)).

Testing

To test this library predicates, load the tester.lgt file:

| ?- logtalk_load(json_path(tester)).

Representation

The library defines the json_path(_Representation_) parametric object where _Representation_ can be one of:

  • atom - member names and normalized paths are represented as atoms

  • chars - member names and normalized paths are represented as chars(List)

  • codes - member names and normalized paths are represented as codes(List)

When using the default json_path object, member names and normalized paths are represented as atoms.

Examples

Parse and evaluate a query:

| ?- json_path::parse(atom('$.store.book[0].title'), Query),
     json_path::evaluate(Query, {store-{book-[{title-'Sayings of the Century'}]}}, Values).
Query = json_path([child([name(store)]), child([name(book)]), child([index(0)]), child([name(title)])])
Values = ['Sayings of the Century']
yes

Filter books by price:

| ?- JSON = {store-{book-[{title-'Sayings', price-8.95}, {title-'Honour', price-12.99}]}},
     json_path::query(atom('$.store.book[?@.price < 10].title'), JSON, Values).
JSON = {store-{book-[{title-'Sayings', price-8.95}, {title-'Honour', price-12.99}]}}
Values = ['Sayings']
yes

Return normalized paths for the selected nodes:

| ?- json_path::parse(atom('$..j'), Query),
     json_path::paths(Query, {o-{j-1}, a-[[{j-4}]]}, Paths).
Paths = ['$[''o''][''j'']', '$[''a''][0][0][''j'']']
yes