Package Exports
- @swaggerexpert/jsonpath
- @swaggerexpert/jsonpath/package.json
Readme
@swaggerexpert/jsonpath
@swaggerexpert/jsonpath is a parser and validator for RFC 9535 Query Expressions for JSON - JSONPath.
The development of this library contributed to the identification and formal submission of Errata 8343 against RFC 9535.
|
Get professionally supported @swaggerexpert/jsonpath with Tidelift Subscription. |
Table of Contents
Getting started
Installation
You can install @swaggerexpert/jsonpath using npm:
$ npm install @swaggerexpert/jsonpathUsage
@swaggerexpert/jsonpath currently supports parsing and validation.
Both parser and validator are based on a superset of ABNF (SABNF)
and use apg-lite parser generator.
Parsing
Parsing a JSONPath Query expression is as simple as importing the parse function and calling it.
import { parse } from '@swaggerexpert/jsonpath';
const parseResult = parse('$.store.book[0].title');or
import { parse, JSONPathQueryCST } from '@swaggerexpert/jsonpath';
const parseResult = parse('$.store.book[0].title', { ast: new JSONPathQueryCST() });parseResult variable has the following shape:
{
result: {
success: true,
state: 101,
stateName: 'MATCH',
length: 21,
matched: 21,
maxMatched: 21,
maxTreeDepth: 21,
nodeHits: 298
},
ast: <JSONPathQueryCST>,
computed: {
stack: [],
root: {
type: 'jsonpath-query',
text: '$.store.book[0].title',
start: 0,
length: 21,
children: [
{
type: 'root-identifier',
text: '$',
start: 0,
length: 1,
children: []
},
{
type: 'segments',
text: '.store.book[0].title',
start: 1,
length: 20,
children: [
{
type: 'segment',
text: '.store',
start: 1,
length: 6,
children: [
{
type: 'child-segment',
text: '.store',
start: 1,
length: 6,
children: [
{
type: 'text',
text: '.',
start: 1,
length: 1,
children: []
},
{
type: 'member-name-shorthand',
text: 'store',
start: 2,
length: 5,
children: []
}
]
}
]
},
{
type: 'segment',
text: '.book',
start: 7,
length: 5,
children: [
{
type: 'child-segment',
text: '.book',
start: 7,
length: 5,
children: [
{
type: 'text',
text: '.',
start: 7,
length: 1,
children: []
},
{
type: 'member-name-shorthand',
text: 'book',
start: 8,
length: 4,
children: []
}
]
}
]
},
{
type: 'segment',
text: '[0]',
start: 12,
length: 3,
children: [
{
type: 'child-segment',
text: '[0]',
start: 12,
length: 3,
children: [
{
type: 'bracketed-selection',
text: '[0]',
start: 12,
length: 3,
children: [
{
type: 'text',
text: '[',
start: 12,
length: 1,
children: []
},
{
type: 'selector',
text: '0',
start: 13,
length: 1,
children: [
{
type: 'index-selector',
text: '0',
start: 13,
length: 1,
children: []
}
]
},
{
type: 'text',
text: ']',
start: 14,
length: 1,
children: []
}
]
}
]
}
]
},
{
type: 'segment',
text: '.title',
start: 15,
length: 6,
children: [
{
type: 'child-segment',
text: '.title',
start: 15,
length: 6,
children: [
{
type: 'text',
text: '.',
start: 15,
length: 1,
children: []
},
{
type: 'member-name-shorthand',
text: 'title',
start: 16,
length: 5,
children: []
}
]
}
]
}
]
}
]
}
}
}Concrete Syntax Tree (CST)
Concrete Syntax Tree (Parse tree) is available on parse result via computed field.
Instance of JSONPathQueryCST needs to be assigned to ast option in parse function (default behavior).
CST is suitable to be consumed by other tools like IDEs, editors, etc...
import { parse } from '@swaggerexpert/jsonpath';
const { computed: CST } = parse('$.store.book[0].title');or
import { parse, JSONPathQueryCST } from '@swaggerexpert/jsonpath';
const { computed: CST } = parse('$.store.book[0].title', { ast: new JSONPathQueryCST() });Interpreting Parse result as XML
import { parse } from '@swaggerexpert/jsonpath';
const parseResult = parse('$.store.book[0].title');
const xml = parseResult.ast.toXml();Parse statistics
parse function returns additional statistical information about the parsing process.
Collection of the statistics can be enabled by setting stats option to true.
import { parse } from '@swaggerexpert/jsonpath';
const { stats } = parse('$.store.book[0].title', { stats: true });
stats.displayStats(); // returns operator stats
stats.displayHits(); // returns rules grouped by hit countErrors
@swaggerexpert/jsonpath provides a structured error class hierarchy,
enabling precise error handling across JSONPath operations, including parsing.
import { JSONPathError, JSONPathParseError } from '@swaggerexpert/jsonpath';JSONPathError is the base class for all JSONPath errors.
Grammar
New grammar instance can be created in following way:
import { Grammar } from '@swaggerexpert/jsonpath';
const grammar = new Grammar();To obtain original ABNF (SABNF) grammar as a string:
import { Grammar } from '@swaggerexpert/jsonpath';
const grammar = new Grammar();
grammar.toString();
// or
String(grammar);More about JSONPath
JSONPath is defined by the following ABNF syntax
; JSONPath: Query Expressions for JSON
; https://www.rfc-editor.org/rfc/rfc9535
; https://www.rfc-editor.org/rfc/rfc9535#section-2.1.1
jsonpath-query = root-identifier segments
segments = *(S segment)
B = %x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
S = *B ; optional blank space
; https://www.rfc-editor.org/rfc/rfc9535#section-2.2.1
root-identifier = "$"
; https://www.rfc-editor.org/rfc/rfc9535#section-2.3
selector = name-selector /
wildcard-selector /
slice-selector /
index-selector /
filter-selector
; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.1.1
name-selector = string-literal
string-literal = dquote *double-quoted dquote / ; "string", MODIFICATION: surrogate text rule used
squote *single-quoted squote ; 'string', MODIFICATION: surrogate text rule used
double-quoted = unescaped /
%x27 / ; '
ESC %x22 / ; \"
ESC escapable
single-quoted = unescaped /
%x22 / ; "
ESC %x27 / ; \'
ESC escapable
ESC = %x5C ; \ backslash
unescaped = %x20-21 / ; see RFC 8259
; omit 0x22 "
%x23-26 /
; omit 0x27 '
%x28-5B /
; omit 0x5C \
%x5D-D7FF /
; skip surrogate code points
%xE000-10FFFF
escapable = %x62 / ; b BS backspace U+0008
%x66 / ; f FF form feed U+000C
%x6E / ; n LF line feed U+000A
%x72 / ; r CR carriage return U+000D
%x74 / ; t HT horizontal tab U+0009
"/" / ; / slash (solidus) U+002F
"\" / ; \ backslash (reverse solidus) U+005C
(%x75 hexchar) ; uXXXX U+XXXX
hexchar = non-surrogate /
(high-surrogate "\" %x75 low-surrogate)
non-surrogate = ((DIGIT / "A"/"B"/"C" / "E"/"F") 3HEXDIG) /
("D" %x30-37 2HEXDIG )
high-surrogate = "D" ("8"/"9"/"A"/"B") 2HEXDIG
low-surrogate = "D" ("C"/"D"/"E"/"F") 2HEXDIG
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.2.1
wildcard-selector = "*"
; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.3.1
index-selector = int ; decimal integer
int = "0" /
(["-"] DIGIT1 *DIGIT) ; - optional
DIGIT1 = %x31-39 ; 1-9 non-zero digit
; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.4.1
slice-selector = [start S] colon S [end S] [colon [S step ]] ; MODIFICATION: surrogate text rule used
start = int ; included in selection
end = int ; not included in selection
step = int ; default: 1
; https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1
filter-selector = questionmark S logical-expr ; MODIFICATION: surrogate text rule used
logical-expr = logical-or-expr
logical-or-expr = logical-and-expr *(S disjunction S logical-and-expr) ; MODIFICATION: surrogate text rule used
; disjunction
; binds less tightly than conjunction
logical-and-expr = basic-expr *(S conjunction S basic-expr) ; MODIFICATION: surrogate text rule used
; conjunction
; binds more tightly than disjunction
basic-expr = paren-expr /
comparison-expr /
test-expr
paren-expr = [logical-not-op S] left-paren S logical-expr S right-paren ; MODIFICATION: surrogate text rule used
; parenthesized expression
logical-not-op = "!" ; logical NOT operator
test-expr = [logical-not-op S]
(filter-query / ; existence/non-existence
function-expr) ; LogicalType or NodesType
filter-query = rel-query / jsonpath-query
rel-query = current-node-identifier segments
current-node-identifier = "@"
comparison-expr = comparable S comparison-op S comparable
literal = number / string-literal /
true / false / null
comparable = singular-query / ; singular query value
function-expr / ; ValueType
literal
; MODIFICATION: https://www.rfc-editor.org/errata/eid8343
comparison-op = "==" / "!=" /
"<=" / ">=" /
"<" / ">"
singular-query = rel-singular-query / abs-singular-query
rel-singular-query = current-node-identifier singular-query-segments
abs-singular-query = root-identifier singular-query-segments
singular-query-segments = *(S (name-segment / index-segment))
name-segment = (left-bracket name-selector right-bracket) / ; MODIFICATION: surrogate text rule used
(dot-prefix member-name-shorthand) ; MODIFICATION: surrogate text rule used
index-segment = left-bracket index-selector right-bracket ; MODIFICATION: surrogate text rule used
number = (int / "-0") [ frac ] [ exp ] ; decimal number
frac = "." 1*DIGIT ; decimal fraction
exp = "e" [ "-" / "+" ] 1*DIGIT ; decimal exponent
true = %x74.72.75.65 ; true
false = %x66.61.6c.73.65 ; false
null = %x6e.75.6c.6c ; null
; https://www.rfc-editor.org/rfc/rfc9535#section-2.4
function-name = function-name-first *function-name-char
function-name-first = LCALPHA
function-name-char = function-name-first / "_" / DIGIT
LCALPHA = %x61-7A ; "a".."z"
function-expr = function-name left-paren S [function-argument ; MODIFICATION: surrogate text rule used
*(S comma S function-argument)] S right-paren ; MODIFICATION: surrogate text rule used
function-argument = logical-expr / ; MODIFICATION: https://www.rfc-editor.org/errata/eid8343
filter-query / ; (includes singular-query)
function-expr /
literal
; https://www.rfc-editor.org/rfc/rfc9535#section-2.5
segment = child-segment / descendant-segment
; https://www.rfc-editor.org/rfc/rfc9535#section-2.5.1.1
child-segment = bracketed-selection /
(dot-prefix ; MODIFICATION: surrogate text rule used
(wildcard-selector /
member-name-shorthand))
bracketed-selection = left-bracket S selector *(S comma S selector) S right-bracket
; MODIFICATION: surrogate text rule used
member-name-shorthand = name-first *name-char
name-first = ALPHA /
"_" /
%x80-D7FF /
; skip surrogate code points
%xE000-10FFFF
name-char = name-first / DIGIT
DIGIT = %x30-39 ; 0-9
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
; https://www.rfc-editor.org/rfc/rfc9535#section-2.5.2.1
descendant-segment = double-dot-prefix (bracketed-selection / ; MODIFICATION: surrogate text rule used
wildcard-selector /
member-name-shorthand)
; https://www.rfc-editor.org/rfc/rfc9535#name-normalized-paths
normalized-path = root-identifier *(normal-index-segment)
normal-index-segment = "[" normal-selector "]"
normal-selector = normal-name-selector / normal-index-selector
normal-name-selector = %x27 *normal-single-quoted %x27 ; 'string'
normal-single-quoted = normal-unescaped /
ESC normal-escapable
normal-unescaped = ; omit %x0-1F control codes
%x20-26 /
; omit 0x27 '
%x28-5B /
; omit 0x5C \
%x5D-D7FF /
; skip surrogate code points
%xE000-10FFFF
normal-escapable = %x62 / ; b BS backspace U+0008
%x66 / ; f FF form feed U+000C
%x6E / ; n LF line feed U+000A
%x72 / ; r CR carriage return U+000D
%x74 / ; t HT horizontal tab U+0009
"'" / ; ' apostrophe U+0027
"\" / ; \ backslash (reverse solidus) U+005C
(%x75 normal-hexchar)
; certain values u00xx U+00XX
normal-hexchar = "0" "0"
(
("0" %x30-37) / ; "00"-"07"
; omit U+0008-U+000A BS HT LF
("0" %x62) / ; "0b"
; omit U+000C-U+000D FF CR
("0" %x65-66) / ; "0e"-"0f"
("1" normal-HEXDIG)
)
normal-HEXDIG = DIGIT / %x61-66 ; "0"-"9", "a"-"f"
normal-index-selector = "0" / (DIGIT1 *DIGIT)
; non-negative decimal integer
; Surrogate named rules
dot-prefix = "."
double-dot-prefix = ".."
left-bracket = "["
right-bracket = "]"
left-paren = "("
right-paren = ")"
comma = ","
colon = ":"
dquote = %x22 ; "
squote = %x27 ; '
questionmark = "?"
disjunction = "||"
conjunction = "&&"License
@swaggerexpert/jsonpath is licensed under Apache 2.0 license.
@swaggerexpert/jsonpath comes with an explicit NOTICE file
containing additional legal notices and information.