json-p3 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/json-p3.cjs.js +312 -72
- package/dist/json-p3.esm.js +312 -73
- package/dist/json-p3.iife.js +312 -72
- package/dist/json-p3.iife.min.js +1 -1
- package/dist/json-p3.iife.min.js.map +1 -1
- package/dist/path/environment.d.ts +35 -10
- package/dist/path/expression.d.ts +1 -0
- package/dist/path/index.d.ts +18 -0
- package/dist/path/node.d.ts +1 -4
- package/dist/path/parse.d.ts +5 -2
- package/dist/path/path.d.ts +6 -0
- package/dist/path/selectors.d.ts +25 -9
- package/dist/path/types.d.ts +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -27
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ console.log(nodes.values()); // [ 'John', 'Sally', 'Jane' ]
|
|
|
39
39
|
|
|
40
40
|
- Docs: https://jg-rp.github.io/json-p3/
|
|
41
41
|
- Install: https://jg-rp.github.io/json-p3/#install
|
|
42
|
+
- JSONPath playground: https://jg-rp.github.io/json-p3/playground
|
|
42
43
|
- JSONPath syntax: https://jg-rp.github.io/json-p3/guides/jsonpath-syntax
|
|
43
44
|
- API reference: https://jg-rp.github.io/json-p3/api
|
|
44
45
|
- Change log: https://github.com/jg-rp/json-p3/blob/main/CHANGELOG.md
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const version = "__VERSION__";
|
|
2
2
|
export * as jsonpath from "./path";
|
|
3
|
-
export { DEFAULT_ENVIRONMENT, FunctionExpressionType, JSONPath, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathSyntaxError, JSONPathTypeError, JSONPathRecursionLimitError, Token, TokenKind, Nothing, query, compile, } from "./path";
|
|
3
|
+
export { DEFAULT_ENVIRONMENT, FunctionExpressionType, JSONPath, JSONPathEnvironment, JSONPathError, JSONPathIndexError, JSONPathLexerError, JSONPathNode, JSONPathNodeList, JSONPathSyntaxError, JSONPathTypeError, JSONPathRecursionLimitError, Token, TokenKind, Nothing, lazyQuery, query, compile, } from "./path";
|
|
4
4
|
export type { JSONPathEnvironmentOptions, FilterFunction } from "./path";
|
|
5
5
|
export * as jsonpointer from "./pointer";
|
|
6
6
|
export { JSONPointer, RelativeJSONPointer, resolve, UNDEFINED, } from "./pointer";
|
package/dist/json-p3.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* json-p3 version 0.
|
|
2
|
+
* json-p3 version 0.3.1
|
|
3
3
|
* https://github.com/jg-rp/json-p3
|
|
4
4
|
*
|
|
5
5
|
* MIT License
|
|
@@ -645,10 +645,6 @@ var index$3 = /*#__PURE__*/Object.freeze({
|
|
|
645
645
|
* The pair of a JSON value and its location found in the target JSON value.
|
|
646
646
|
*/
|
|
647
647
|
class JSONPathNode {
|
|
648
|
-
/**
|
|
649
|
-
* The normalized path to this node in the target JSON value.
|
|
650
|
-
*/
|
|
651
|
-
|
|
652
648
|
/**
|
|
653
649
|
* @param value - The JSON value found at _location_.
|
|
654
650
|
* @param location - The parts of a normalized path to _value_.
|
|
@@ -658,9 +654,12 @@ class JSONPathNode {
|
|
|
658
654
|
this.value = value;
|
|
659
655
|
this.location = location;
|
|
660
656
|
this.root = root;
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
657
|
+
}
|
|
658
|
+
get path() {
|
|
659
|
+
return (
|
|
660
|
+
// eslint-disable-next-line prefer-template
|
|
661
|
+
"$" + this.location.map(s => isString(s) ? `['${s}']` : `[${s}]`).join("")
|
|
662
|
+
);
|
|
664
663
|
}
|
|
665
664
|
|
|
666
665
|
/**
|
|
@@ -912,7 +911,7 @@ class JSONPathQuery extends FilterExpression {
|
|
|
912
911
|
}
|
|
913
912
|
class RelativeQuery extends JSONPathQuery {
|
|
914
913
|
evaluate(context) {
|
|
915
|
-
return this.path.query(context.currentValue);
|
|
914
|
+
return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.currentValue))) : this.path.query(context.currentValue);
|
|
916
915
|
}
|
|
917
916
|
toString() {
|
|
918
917
|
return `@${this.path.toString().slice(1)}`;
|
|
@@ -920,7 +919,7 @@ class RelativeQuery extends JSONPathQuery {
|
|
|
920
919
|
}
|
|
921
920
|
class RootQuery extends JSONPathQuery {
|
|
922
921
|
evaluate(context) {
|
|
923
|
-
return this.path.query(context.rootValue);
|
|
922
|
+
return context.lazy ? new JSONPathNodeList(Array.from(this.path.lazyQuery(context.rootValue))) : this.path.query(context.rootValue);
|
|
924
923
|
}
|
|
925
924
|
toString() {
|
|
926
925
|
return this.path.toString();
|
|
@@ -938,12 +937,26 @@ class FunctionExtension extends FilterExpression {
|
|
|
938
937
|
if (!func) {
|
|
939
938
|
throw new UndefinedFilterFunctionError(`filter function '${this.name}' is undefined`, this.token);
|
|
940
939
|
}
|
|
941
|
-
const args = this.args.map(arg => arg.evaluate(context)).map((arg, idx) => func.argTypes[idx] !== FunctionExpressionType.NodesType && arg instanceof JSONPathNodeList ?
|
|
940
|
+
const args = this.args.map(arg => arg.evaluate(context)).map((arg, idx) => func.argTypes[idx] !== FunctionExpressionType.NodesType && arg instanceof JSONPathNodeList ? this.unpack_node_list(arg) : arg);
|
|
942
941
|
return func.call(...args);
|
|
943
942
|
}
|
|
944
943
|
toString() {
|
|
945
944
|
return `${this.name}(${this.args.map(e => e.toString()).join(", ")})`;
|
|
946
945
|
}
|
|
946
|
+
unpack_node_list(arg) {
|
|
947
|
+
switch (arg.length) {
|
|
948
|
+
case 0:
|
|
949
|
+
// If the query results in an empty node list, the argument
|
|
950
|
+
// is the special result Nothing.
|
|
951
|
+
return Nothing;
|
|
952
|
+
case 1:
|
|
953
|
+
// If the query results in a node list consisting of a single
|
|
954
|
+
// node, the argument is the value of the node
|
|
955
|
+
return arg.nodes[0].value;
|
|
956
|
+
default:
|
|
957
|
+
return arg;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
947
960
|
}
|
|
948
961
|
|
|
949
962
|
/**
|
|
@@ -1722,6 +1735,10 @@ class JSONPathSelector {
|
|
|
1722
1735
|
this.token = token;
|
|
1723
1736
|
}
|
|
1724
1737
|
|
|
1738
|
+
/**
|
|
1739
|
+
* @param nodes - Nodes matched by preceding selectors.
|
|
1740
|
+
*/
|
|
1741
|
+
|
|
1725
1742
|
/**
|
|
1726
1743
|
* @param nodes - Nodes matched by preceding selectors.
|
|
1727
1744
|
*/
|
|
@@ -1749,7 +1766,14 @@ class NameSelector extends JSONPathSelector {
|
|
|
1749
1766
|
rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
|
|
1750
1767
|
}
|
|
1751
1768
|
}
|
|
1752
|
-
return
|
|
1769
|
+
return rv;
|
|
1770
|
+
}
|
|
1771
|
+
*lazyResolve(nodes) {
|
|
1772
|
+
for (const node of nodes) {
|
|
1773
|
+
if (hasStringKey(node.value, this.name)) {
|
|
1774
|
+
yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1753
1777
|
}
|
|
1754
1778
|
toString() {
|
|
1755
1779
|
return this.shorthand ? `['${this.name}']` : `'${this.name}'`;
|
|
@@ -1779,7 +1803,17 @@ class IndexSelector extends JSONPathSelector {
|
|
|
1779
1803
|
}
|
|
1780
1804
|
}
|
|
1781
1805
|
}
|
|
1782
|
-
return
|
|
1806
|
+
return rv;
|
|
1807
|
+
}
|
|
1808
|
+
*lazyResolve(nodes) {
|
|
1809
|
+
for (const node of nodes) {
|
|
1810
|
+
if (isArray(node.value)) {
|
|
1811
|
+
const normIndex = this.normalizedIndex(node.value.length);
|
|
1812
|
+
if (normIndex in node.value) {
|
|
1813
|
+
yield new JSONPathNode(node.value[normIndex], node.location.concat(normIndex), node.root);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1783
1817
|
}
|
|
1784
1818
|
toString() {
|
|
1785
1819
|
return String(this.index);
|
|
@@ -1807,7 +1841,15 @@ class SliceSelector extends JSONPathSelector {
|
|
|
1807
1841
|
rv.push(new JSONPathNode(value, node.location.concat(i), node.root));
|
|
1808
1842
|
}
|
|
1809
1843
|
}
|
|
1810
|
-
return
|
|
1844
|
+
return rv;
|
|
1845
|
+
}
|
|
1846
|
+
*lazyResolve(nodes) {
|
|
1847
|
+
for (const node of nodes) {
|
|
1848
|
+
if (!isArray(node.value)) continue;
|
|
1849
|
+
for (const [i, value] of this.slice(node.value, this.start, this.stop, this.step)) {
|
|
1850
|
+
yield new JSONPathNode(value, node.location.concat(i), node.root);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1811
1853
|
}
|
|
1812
1854
|
toString() {
|
|
1813
1855
|
const start = this.start ? this.start : "";
|
|
@@ -1890,22 +1932,92 @@ class WildcardSelector extends JSONPathSelector {
|
|
|
1890
1932
|
}
|
|
1891
1933
|
}
|
|
1892
1934
|
}
|
|
1893
|
-
return
|
|
1935
|
+
return rv;
|
|
1936
|
+
}
|
|
1937
|
+
*lazyResolve(nodes) {
|
|
1938
|
+
for (const node of nodes) {
|
|
1939
|
+
if (node.value instanceof String) continue;
|
|
1940
|
+
if (isArray(node.value)) {
|
|
1941
|
+
for (let i = 0; i < node.value.length; i++) {
|
|
1942
|
+
yield new JSONPathNode(node.value[i], node.location.concat(i), node.root);
|
|
1943
|
+
}
|
|
1944
|
+
} else if (isObject(node.value)) {
|
|
1945
|
+
for (const [key, value] of Object.entries(node.value)) {
|
|
1946
|
+
yield new JSONPathNode(value, node.location.concat(key), node.root);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1894
1950
|
}
|
|
1895
1951
|
toString() {
|
|
1896
1952
|
return this.shorthand ? "[*]" : "*";
|
|
1897
1953
|
}
|
|
1898
1954
|
}
|
|
1899
1955
|
class RecursiveDescentSegment extends JSONPathSelector {
|
|
1956
|
+
constructor(environment, token, selector) {
|
|
1957
|
+
super(environment, token);
|
|
1958
|
+
this.environment = environment;
|
|
1959
|
+
this.token = token;
|
|
1960
|
+
this.selector = selector;
|
|
1961
|
+
}
|
|
1900
1962
|
resolve(nodes) {
|
|
1901
1963
|
const rv = [];
|
|
1902
1964
|
for (const node of nodes) {
|
|
1903
|
-
rv.push(node
|
|
1965
|
+
rv.push(node);
|
|
1966
|
+
for (const _node of this.visit(node)) {
|
|
1967
|
+
rv.push(_node);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
return this.selector.resolve(rv);
|
|
1971
|
+
}
|
|
1972
|
+
*lazyResolve(nodes) {
|
|
1973
|
+
yield* this.selector.lazyResolve(this._lazyResolve(nodes));
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
1977
|
+
*_lazyResolve(nodes) {
|
|
1978
|
+
for (const _node of nodes) {
|
|
1979
|
+
const stack = [{
|
|
1980
|
+
node: _node,
|
|
1981
|
+
depth: 0
|
|
1982
|
+
}];
|
|
1983
|
+
yield _node;
|
|
1984
|
+
while (stack.length) {
|
|
1985
|
+
const {
|
|
1986
|
+
node: currentNode,
|
|
1987
|
+
depth
|
|
1988
|
+
} = stack.pop();
|
|
1989
|
+
if (depth >= this.environment.maxRecursionDepth) {
|
|
1990
|
+
throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
|
|
1991
|
+
}
|
|
1992
|
+
if (currentNode.value instanceof String) continue;
|
|
1993
|
+
if (isArray(currentNode.value)) {
|
|
1994
|
+
for (let i = 0; i < currentNode.value.length; i++) {
|
|
1995
|
+
const __node = new JSONPathNode(currentNode.value[i], currentNode.location.concat(i), currentNode.root);
|
|
1996
|
+
yield __node;
|
|
1997
|
+
if (isObject(__node.value)) {
|
|
1998
|
+
stack.push({
|
|
1999
|
+
node: __node,
|
|
2000
|
+
depth: depth + 1
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
} else if (isObject(currentNode.value)) {
|
|
2005
|
+
for (const [key, value] of Object.entries(currentNode.value)) {
|
|
2006
|
+
const __node = new JSONPathNode(value, currentNode.location.concat(key), currentNode.root);
|
|
2007
|
+
yield __node;
|
|
2008
|
+
if (isObject(__node.value)) {
|
|
2009
|
+
stack.push({
|
|
2010
|
+
node: __node,
|
|
2011
|
+
depth: depth + 1
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
1904
2017
|
}
|
|
1905
|
-
return new JSONPathNodeList(rv);
|
|
1906
2018
|
}
|
|
1907
2019
|
toString() {
|
|
1908
|
-
return
|
|
2020
|
+
return `..${this.selector.toString()}`;
|
|
1909
2021
|
}
|
|
1910
2022
|
visit(node) {
|
|
1911
2023
|
let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
@@ -1913,19 +2025,25 @@ class RecursiveDescentSegment extends JSONPathSelector {
|
|
|
1913
2025
|
throw new JSONPathRecursionLimitError("recursion limit reached", this.token);
|
|
1914
2026
|
}
|
|
1915
2027
|
const rv = [];
|
|
1916
|
-
if (node.value instanceof String) return
|
|
2028
|
+
if (node.value instanceof String) return rv;
|
|
1917
2029
|
if (isArray(node.value)) {
|
|
1918
2030
|
for (let i = 0; i < node.value.length; i++) {
|
|
1919
2031
|
const _node = new JSONPathNode(node.value[i], node.location.concat(i), node.root);
|
|
1920
|
-
rv.push(_node
|
|
2032
|
+
rv.push(_node);
|
|
2033
|
+
for (const __node of this.visit(_node, depth + 1)) {
|
|
2034
|
+
rv.push(__node);
|
|
2035
|
+
}
|
|
1921
2036
|
}
|
|
1922
2037
|
} else if (isObject(node.value)) {
|
|
1923
2038
|
for (const [key, value] of Object.entries(node.value)) {
|
|
1924
2039
|
const _node = new JSONPathNode(value, node.location.concat(key), node.root);
|
|
1925
|
-
rv.push(_node
|
|
2040
|
+
rv.push(_node);
|
|
2041
|
+
for (const __node of this.visit(_node, depth + 1)) {
|
|
2042
|
+
rv.push(__node);
|
|
2043
|
+
}
|
|
1926
2044
|
}
|
|
1927
2045
|
}
|
|
1928
|
-
return
|
|
2046
|
+
return rv;
|
|
1929
2047
|
}
|
|
1930
2048
|
}
|
|
1931
2049
|
class FilterSelector extends JSONPathSelector {
|
|
@@ -1966,7 +2084,40 @@ class FilterSelector extends JSONPathSelector {
|
|
|
1966
2084
|
}
|
|
1967
2085
|
}
|
|
1968
2086
|
}
|
|
1969
|
-
return
|
|
2087
|
+
return rv;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
2091
|
+
*lazyResolve(nodes) {
|
|
2092
|
+
for (const node of nodes) {
|
|
2093
|
+
if (node.value instanceof String) continue;
|
|
2094
|
+
if (isArray(node.value)) {
|
|
2095
|
+
for (let i = 0; i < node.value.length; i++) {
|
|
2096
|
+
const value = node.value[i];
|
|
2097
|
+
const filterContext = {
|
|
2098
|
+
environment: this.environment,
|
|
2099
|
+
currentValue: value,
|
|
2100
|
+
rootValue: node.root,
|
|
2101
|
+
lazy: true
|
|
2102
|
+
};
|
|
2103
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2104
|
+
yield new JSONPathNode(value, node.location.concat(i), node.root);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
} else if (isObject(node.value)) {
|
|
2108
|
+
for (const [key, value] of Object.entries(node.value)) {
|
|
2109
|
+
const filterContext = {
|
|
2110
|
+
environment: this.environment,
|
|
2111
|
+
currentValue: value,
|
|
2112
|
+
rootValue: node.root,
|
|
2113
|
+
lazy: true
|
|
2114
|
+
};
|
|
2115
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2116
|
+
yield new JSONPathNode(value, node.location.concat(key), node.root);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
1970
2121
|
}
|
|
1971
2122
|
toString() {
|
|
1972
2123
|
return `?${this.expression.toString()}`;
|
|
@@ -1983,10 +2134,19 @@ class BracketedSelection extends JSONPathSelector {
|
|
|
1983
2134
|
const rv = [];
|
|
1984
2135
|
for (const node of nodes) {
|
|
1985
2136
|
for (const item of this.items) {
|
|
1986
|
-
|
|
2137
|
+
for (const _node of item.resolve([node])) {
|
|
2138
|
+
rv.push(_node);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
return rv;
|
|
2143
|
+
}
|
|
2144
|
+
*lazyResolve(nodes) {
|
|
2145
|
+
for (const node of nodes) {
|
|
2146
|
+
for (const item of this.items) {
|
|
2147
|
+
yield* item.lazyResolve([node]);
|
|
1987
2148
|
}
|
|
1988
2149
|
}
|
|
1989
|
-
return new JSONPathNodeList(rv);
|
|
1990
2150
|
}
|
|
1991
2151
|
toString() {
|
|
1992
2152
|
return `[${this.items.map(itm => itm.toString()).join(", ")}]`;
|
|
@@ -2025,10 +2185,23 @@ class JSONPath {
|
|
|
2025
2185
|
* @returns
|
|
2026
2186
|
*/
|
|
2027
2187
|
query(value) {
|
|
2028
|
-
let nodes =
|
|
2188
|
+
let nodes = [new JSONPathNode(value, [], value)];
|
|
2029
2189
|
for (const selector of this.selectors) {
|
|
2030
2190
|
nodes = selector.resolve(nodes);
|
|
2031
2191
|
}
|
|
2192
|
+
return new JSONPathNodeList(nodes);
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
/**
|
|
2196
|
+
*
|
|
2197
|
+
* @param value -
|
|
2198
|
+
* @returns
|
|
2199
|
+
*/
|
|
2200
|
+
lazyQuery(value) {
|
|
2201
|
+
let nodes = [new JSONPathNode(value, [], value)][Symbol.iterator]();
|
|
2202
|
+
for (const selector of this.selectors) {
|
|
2203
|
+
nodes = selector.lazyResolve(nodes);
|
|
2204
|
+
}
|
|
2032
2205
|
return nodes;
|
|
2033
2206
|
}
|
|
2034
2207
|
|
|
@@ -2041,7 +2214,10 @@ class JSONPath {
|
|
|
2041
2214
|
* there are no matches.
|
|
2042
2215
|
*/
|
|
2043
2216
|
match(value) {
|
|
2044
|
-
|
|
2217
|
+
const it = this.lazyQuery(value);
|
|
2218
|
+
const rv = it.next();
|
|
2219
|
+
if (rv.done) return undefined;
|
|
2220
|
+
return rv.value;
|
|
2045
2221
|
}
|
|
2046
2222
|
|
|
2047
2223
|
/**
|
|
@@ -2061,13 +2237,17 @@ class JSONPath {
|
|
|
2061
2237
|
}
|
|
2062
2238
|
|
|
2063
2239
|
const PRECEDENCE_LOWEST = 1;
|
|
2064
|
-
const PRECEDENCE_LOGICALRIGHT = 3;
|
|
2065
2240
|
const PRECEDENCE_LOGICAL_AND = 4;
|
|
2066
2241
|
const PRECEDENCE_LOGICAL_OR = 5;
|
|
2067
2242
|
const PRECEDENCE_COMPARISON = 6;
|
|
2068
|
-
const
|
|
2243
|
+
const PRECEDENCE_PREFIX = 7;
|
|
2244
|
+
const PRECEDENCES = new Map([[TokenKind.AND, PRECEDENCE_LOGICAL_AND], [TokenKind.EQ, PRECEDENCE_COMPARISON], [TokenKind.GE, PRECEDENCE_COMPARISON], [TokenKind.GT, PRECEDENCE_COMPARISON], [TokenKind.LE, PRECEDENCE_COMPARISON], [TokenKind.LT, PRECEDENCE_COMPARISON], [TokenKind.NE, PRECEDENCE_COMPARISON], [TokenKind.NOT, PRECEDENCE_PREFIX], [TokenKind.OR, PRECEDENCE_LOGICAL_OR], [TokenKind.RPAREN, PRECEDENCE_LOWEST]]);
|
|
2069
2245
|
const BINARY_OPERATORS = new Map([[TokenKind.AND, "&&"], [TokenKind.EQ, "=="], [TokenKind.GE, ">="], [TokenKind.GT, ">"], [TokenKind.LE, "<="], [TokenKind.LT, "<"], [TokenKind.NE, "!="], [TokenKind.OR, "||"]]);
|
|
2070
2246
|
const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
|
|
2247
|
+
|
|
2248
|
+
/**
|
|
2249
|
+
* JSONPath token stream parser.
|
|
2250
|
+
*/
|
|
2071
2251
|
class Parser {
|
|
2072
2252
|
constructor(environment) {
|
|
2073
2253
|
this.environment = environment;
|
|
@@ -2084,30 +2264,41 @@ class Parser {
|
|
|
2084
2264
|
parsePath(stream) {
|
|
2085
2265
|
let inFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
2086
2266
|
const selectors = [];
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
break;
|
|
2095
|
-
case TokenKind.DDOT:
|
|
2096
|
-
selectors.push(new RecursiveDescentSegment(this.environment, stream.current));
|
|
2097
|
-
break;
|
|
2098
|
-
case TokenKind.LBRACKET:
|
|
2099
|
-
selectors.push(this.parseBracketedSelection(stream));
|
|
2100
|
-
break;
|
|
2101
|
-
default:
|
|
2102
|
-
if (inFilter) {
|
|
2103
|
-
stream.backup();
|
|
2104
|
-
}
|
|
2105
|
-
break loop;
|
|
2267
|
+
for (;;) {
|
|
2268
|
+
const selector = this.parseSegment(stream);
|
|
2269
|
+
if (!selector) {
|
|
2270
|
+
if (inFilter) {
|
|
2271
|
+
stream.backup();
|
|
2272
|
+
}
|
|
2273
|
+
break;
|
|
2106
2274
|
}
|
|
2275
|
+
selectors.push(selector);
|
|
2107
2276
|
stream.next();
|
|
2108
2277
|
}
|
|
2109
2278
|
return selectors;
|
|
2110
2279
|
}
|
|
2280
|
+
parseSegment(stream) {
|
|
2281
|
+
switch (stream.current.kind) {
|
|
2282
|
+
case TokenKind.NAME:
|
|
2283
|
+
return new NameSelector(this.environment, stream.current, stream.current.value, true);
|
|
2284
|
+
case TokenKind.WILD:
|
|
2285
|
+
return new WildcardSelector(this.environment, stream.current, true);
|
|
2286
|
+
case TokenKind.DDOT:
|
|
2287
|
+
{
|
|
2288
|
+
const segmentToken = stream.current;
|
|
2289
|
+
stream.next();
|
|
2290
|
+
const selector = this.parseSegment(stream);
|
|
2291
|
+
if (!selector) {
|
|
2292
|
+
throw new JSONPathSyntaxError("bald descendant segment", stream.current);
|
|
2293
|
+
}
|
|
2294
|
+
return new RecursiveDescentSegment(this.environment, segmentToken, selector);
|
|
2295
|
+
}
|
|
2296
|
+
case TokenKind.LBRACKET:
|
|
2297
|
+
return this.parseBracketedSelection(stream);
|
|
2298
|
+
default:
|
|
2299
|
+
return null;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2111
2302
|
parseIndex(stream) {
|
|
2112
2303
|
if (stream.current.value.length > 1 && stream.current.value.startsWith("0") || stream.current.value.startsWith("-0")) {
|
|
2113
2304
|
throw new JSONPathSyntaxError("leading zero in index selector", stream.current);
|
|
@@ -2228,7 +2419,7 @@ class Parser {
|
|
|
2228
2419
|
parsePrefixExpression(stream) {
|
|
2229
2420
|
stream.expect(TokenKind.NOT);
|
|
2230
2421
|
stream.next();
|
|
2231
|
-
return new PrefixExpression(stream.current, "!", this.parseFilterExpression(stream,
|
|
2422
|
+
return new PrefixExpression(stream.current, "!", this.parseFilterExpression(stream, PRECEDENCE_PREFIX));
|
|
2232
2423
|
}
|
|
2233
2424
|
parseInfixExpression(stream, left) {
|
|
2234
2425
|
const tok = stream.next();
|
|
@@ -2238,11 +2429,9 @@ class Parser {
|
|
|
2238
2429
|
if (!operator) {
|
|
2239
2430
|
throw new JSONPathSyntaxError(`unknown operator '${tok.kind}'`, tok);
|
|
2240
2431
|
}
|
|
2241
|
-
this.throwForNonSingularQuery(left);
|
|
2242
|
-
this.throwForNonSingularQuery(right);
|
|
2243
2432
|
if (COMPARISON_OPERATORS.has(operator)) {
|
|
2244
|
-
this.
|
|
2245
|
-
this.
|
|
2433
|
+
this.throwForNonComparable(left);
|
|
2434
|
+
this.throwForNonComparable(right);
|
|
2246
2435
|
}
|
|
2247
2436
|
return new InfixExpression(tok, left, operator, right);
|
|
2248
2437
|
}
|
|
@@ -2330,16 +2519,15 @@ class Parser {
|
|
|
2330
2519
|
throw new JSONPathSyntaxError(`invalid ${isName ? "name selector" : "string literal"} '${token.value}'`, token);
|
|
2331
2520
|
}
|
|
2332
2521
|
}
|
|
2333
|
-
|
|
2522
|
+
throwForNonComparable(expr) {
|
|
2334
2523
|
if ((expr instanceof RootQuery || expr instanceof RelativeQuery) && !expr.path.singularQuery()) {
|
|
2335
|
-
throw new
|
|
2524
|
+
throw new JSONPathTypeError("non-singular query is not comparable", expr.token);
|
|
2336
2525
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
throw new JSONPathTypeError(`result of ${expr.name}() is not comparable`, expr.token);
|
|
2526
|
+
if (expr instanceof FunctionExtension) {
|
|
2527
|
+
const func = this.environment.functionRegister.get(expr.name);
|
|
2528
|
+
if (func && func.returnType !== FunctionExpressionType.ValueType) {
|
|
2529
|
+
throw new JSONPathTypeError(`result of ${expr.name}() is not comparable`, expr.token);
|
|
2530
|
+
}
|
|
2343
2531
|
}
|
|
2344
2532
|
}
|
|
2345
2533
|
}
|
|
@@ -2350,7 +2538,10 @@ class Parser {
|
|
|
2350
2538
|
*/
|
|
2351
2539
|
|
|
2352
2540
|
/**
|
|
2541
|
+
* A configuration object from which JSONPath queries can be evaluated.
|
|
2353
2542
|
*
|
|
2543
|
+
* An environment is where you'd register custom function extensions or set
|
|
2544
|
+
* the maximum recursion depth limit, for example.
|
|
2354
2545
|
*/
|
|
2355
2546
|
class JSONPathEnvironment {
|
|
2356
2547
|
/**
|
|
@@ -2383,8 +2574,7 @@ class JSONPathEnvironment {
|
|
|
2383
2574
|
*/
|
|
2384
2575
|
functionRegister = new Map();
|
|
2385
2576
|
/**
|
|
2386
|
-
*
|
|
2387
|
-
* @param options -
|
|
2577
|
+
* @param options - Environment configuration options.
|
|
2388
2578
|
*/
|
|
2389
2579
|
constructor() {
|
|
2390
2580
|
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -2397,9 +2587,8 @@ class JSONPathEnvironment {
|
|
|
2397
2587
|
}
|
|
2398
2588
|
|
|
2399
2589
|
/**
|
|
2400
|
-
*
|
|
2401
|
-
* @
|
|
2402
|
-
* @returns
|
|
2590
|
+
* @param path - A JSONPath query to parse.
|
|
2591
|
+
* @returns A new {@link JSONPath} object, bound to this environment.
|
|
2403
2592
|
*/
|
|
2404
2593
|
compile(path) {
|
|
2405
2594
|
return new JSONPath(this, this.parser.parse(new TokenStream(tokenize(path))));
|
|
@@ -2407,14 +2596,28 @@ class JSONPathEnvironment {
|
|
|
2407
2596
|
|
|
2408
2597
|
/**
|
|
2409
2598
|
*
|
|
2410
|
-
* @param path -
|
|
2411
|
-
* @param value -
|
|
2412
|
-
* @returns
|
|
2599
|
+
* @param path - A JSONPath query to parse and evaluate against _value_.
|
|
2600
|
+
* @param value - Data to which _path_ will be applied.
|
|
2601
|
+
* @returns The {@link JSONPathNodeList} resulting from applying _path_
|
|
2602
|
+
* to _value_.
|
|
2413
2603
|
*/
|
|
2414
2604
|
query(path, value) {
|
|
2415
2605
|
return this.compile(path).query(value);
|
|
2416
2606
|
}
|
|
2417
2607
|
|
|
2608
|
+
/**
|
|
2609
|
+
* A lazy version of {@link query} which is faster and more memory
|
|
2610
|
+
* efficient when querying some large datasets.
|
|
2611
|
+
*
|
|
2612
|
+
* @param path - A JSONPath query to parse and evaluate against _value_.
|
|
2613
|
+
* @param value - Data to which _path_ will be applied.
|
|
2614
|
+
* @returns A sequence of {@link JSONPathNode} objects resulting from
|
|
2615
|
+
* applying _path_ to _value_.
|
|
2616
|
+
*/
|
|
2617
|
+
lazyQuery(path, value) {
|
|
2618
|
+
return this.compile(path).lazyQuery(value);
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2418
2621
|
/**
|
|
2419
2622
|
* Return a {@link JSONPathNode} instance for the first object found in
|
|
2420
2623
|
* _value_ matching _path_.
|
|
@@ -2427,6 +2630,11 @@ class JSONPathEnvironment {
|
|
|
2427
2630
|
match(path, value) {
|
|
2428
2631
|
return this.compile(path).match(value);
|
|
2429
2632
|
}
|
|
2633
|
+
|
|
2634
|
+
/**
|
|
2635
|
+
* A hook for setting up the function register. You are encouraged to
|
|
2636
|
+
* override this method in classes extending `JSONPathEnvironment`.
|
|
2637
|
+
*/
|
|
2430
2638
|
setupFilterFunctions() {
|
|
2431
2639
|
this.functionRegister.set("count", new Count());
|
|
2432
2640
|
this.functionRegister.set("length", new Length());
|
|
@@ -2436,9 +2644,18 @@ class JSONPathEnvironment {
|
|
|
2436
2644
|
}
|
|
2437
2645
|
|
|
2438
2646
|
/**
|
|
2647
|
+
* Check the well-typedness of a function's arguments at compile-time.
|
|
2648
|
+
*
|
|
2649
|
+
* This method is called by the {@link Parser} when parsing function calls.
|
|
2650
|
+
* It is expected to throw a {@link JSONPathTypeError} if the function's
|
|
2651
|
+
* parameters are not well-typed.
|
|
2652
|
+
*
|
|
2653
|
+
* Override this if you want to deviate from the JSONPath Spec's function
|
|
2654
|
+
* extension type system.
|
|
2439
2655
|
*
|
|
2440
|
-
* @param token -
|
|
2441
|
-
*
|
|
2656
|
+
* @param token - The {@link Token} starting the function call. `Token.value`
|
|
2657
|
+
* will contain the name of the function.
|
|
2658
|
+
* @param args - One {@link FilterExpression} for each argument.
|
|
2442
2659
|
*/
|
|
2443
2660
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
2444
2661
|
checkWellTypedness(token, args) {
|
|
@@ -2505,6 +2722,27 @@ function query(path, value) {
|
|
|
2505
2722
|
return DEFAULT_ENVIRONMENT.query(path, value);
|
|
2506
2723
|
}
|
|
2507
2724
|
|
|
2725
|
+
/**
|
|
2726
|
+
* Lazily query JSON value _value_ with JSONPath expression _path_.
|
|
2727
|
+
* Lazy queries can be faster and more memory efficient when querying
|
|
2728
|
+
* large datasets, especially when using recursive decent selectors.
|
|
2729
|
+
*
|
|
2730
|
+
* @param path - A JSONPath expression/query.
|
|
2731
|
+
* @param value - The JSON-like value the JSONPath query is applied to.
|
|
2732
|
+
* @returns A sequence of {@link JSONPathNode} objects resulting from
|
|
2733
|
+
* applying _path_ to _value_.
|
|
2734
|
+
*
|
|
2735
|
+
* @throws {@link JSONPathSyntaxError}
|
|
2736
|
+
* If the path does not conform to standard syntax.
|
|
2737
|
+
*
|
|
2738
|
+
* @throws {@link JSONPathTypeError}
|
|
2739
|
+
* If filter function arguments are invalid, or filter expression are
|
|
2740
|
+
* used in an invalid way.
|
|
2741
|
+
*/
|
|
2742
|
+
function lazyQuery(path, value) {
|
|
2743
|
+
return DEFAULT_ENVIRONMENT.lazyQuery(path, value);
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2508
2746
|
/**
|
|
2509
2747
|
* Compile JSONPath _path_ for later use.
|
|
2510
2748
|
* @param path - A JSONPath expression/query.
|
|
@@ -2554,6 +2792,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
2554
2792
|
compile: compile,
|
|
2555
2793
|
expressions: expression,
|
|
2556
2794
|
functions: index$2,
|
|
2795
|
+
lazyQuery: lazyQuery,
|
|
2557
2796
|
match: match,
|
|
2558
2797
|
query: query,
|
|
2559
2798
|
selectors: selectors
|
|
@@ -3039,7 +3278,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3039
3278
|
apply: apply
|
|
3040
3279
|
});
|
|
3041
3280
|
|
|
3042
|
-
const version = "0.
|
|
3281
|
+
const version = "0.3.1";
|
|
3043
3282
|
|
|
3044
3283
|
exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
|
|
3045
3284
|
exports.FunctionExpressionType = FunctionExpressionType;
|
|
@@ -3067,6 +3306,7 @@ exports.compile = compile;
|
|
|
3067
3306
|
exports.jsonpatch = index;
|
|
3068
3307
|
exports.jsonpath = index$1;
|
|
3069
3308
|
exports.jsonpointer = index$3;
|
|
3309
|
+
exports.lazyQuery = lazyQuery;
|
|
3070
3310
|
exports.query = query;
|
|
3071
3311
|
exports.resolve = resolve;
|
|
3072
3312
|
exports.version = version;
|