json-p3 1.2.0 → 1.3.0
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/dist/json-p3.cjs.js +255 -53
- package/dist/json-p3.esm.js +255 -53
- package/dist/json-p3.iife.js +255 -53
- package/dist/json-p3.iife.min.js +1 -1
- package/dist/json-p3.iife.min.js.map +1 -1
- package/dist/path/extra/selectors.d.ts +20 -0
- package/dist/path/index.d.ts +1 -1
- package/dist/path/lex.d.ts +1 -0
- package/dist/path/node.d.ts +1 -0
- package/dist/path/parse.d.ts +1 -1
- package/dist/path/selectors.d.ts +1 -0
- package/dist/path/token.d.ts +7 -3
- package/dist/path/types.d.ts +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/json-p3.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* json-p3 version 1.
|
|
2
|
+
* json-p3 version 1.3.0
|
|
3
3
|
* https://github.com/jg-rp/json-p3
|
|
4
4
|
*
|
|
5
5
|
* MIT License
|
|
@@ -641,6 +641,24 @@ var index$3 = /*#__PURE__*/Object.freeze({
|
|
|
641
641
|
resolve: resolve
|
|
642
642
|
});
|
|
643
643
|
|
|
644
|
+
const Nothing = Symbol.for("jsonpath.nothing");
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* ValueType for JSONPath function expression tye system.
|
|
648
|
+
*/
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Object passed to FilterExpression.evaluate().
|
|
652
|
+
*/
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* A type predicate for an object with a string property.
|
|
656
|
+
*/
|
|
657
|
+
function hasStringKey(value, key) {
|
|
658
|
+
return isObject(value) && Object.hasOwn(value, key);
|
|
659
|
+
}
|
|
660
|
+
const KEY_MARK = "\x02";
|
|
661
|
+
|
|
644
662
|
/**
|
|
645
663
|
* The pair of a JSON value and its location found in the target JSON value.
|
|
646
664
|
*/
|
|
@@ -658,7 +676,7 @@ class JSONPathNode {
|
|
|
658
676
|
get path() {
|
|
659
677
|
return (
|
|
660
678
|
// eslint-disable-next-line prefer-template
|
|
661
|
-
"$" + this.location.map(s => isString(s) ?
|
|
679
|
+
"$" + this.location.map(s => isString(s) ? this.decode_name_location(s) : `[${s}]`).join("")
|
|
662
680
|
);
|
|
663
681
|
}
|
|
664
682
|
|
|
@@ -671,6 +689,9 @@ class JSONPathNode {
|
|
|
671
689
|
}
|
|
672
690
|
return new JSONPointer(JSONPointer.encode(this.location.map(String)));
|
|
673
691
|
}
|
|
692
|
+
decode_name_location(name) {
|
|
693
|
+
return name.startsWith(KEY_MARK) ? `[~'${name.slice(1).replaceAll("'", "\\'")}']` : `['${name.replaceAll("'", "\\'")}']`;
|
|
694
|
+
}
|
|
674
695
|
}
|
|
675
696
|
|
|
676
697
|
/**
|
|
@@ -750,23 +771,6 @@ class JSONPathNodeList {
|
|
|
750
771
|
}
|
|
751
772
|
}
|
|
752
773
|
|
|
753
|
-
const Nothing = Symbol.for("jsonpath.nothing");
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* ValueType for JSONPath function expression tye system.
|
|
757
|
-
*/
|
|
758
|
-
|
|
759
|
-
/**
|
|
760
|
-
* Object passed to FilterExpression.evaluate().
|
|
761
|
-
*/
|
|
762
|
-
|
|
763
|
-
/**
|
|
764
|
-
* A type predicate for an object with a string property.
|
|
765
|
-
*/
|
|
766
|
-
function hasStringKey(value, key) {
|
|
767
|
-
return isObject(value) && Object.hasOwn(value, key);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
774
|
/**
|
|
771
775
|
* Base class for all filter expressions.
|
|
772
776
|
*/
|
|
@@ -1169,7 +1173,8 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
|
|
|
1169
1173
|
TokenKind["AND"] = "TOKEN_AND";
|
|
1170
1174
|
TokenKind["COLON"] = "TOKEN_COLON";
|
|
1171
1175
|
TokenKind["COMMA"] = "TOKEN_COMMA";
|
|
1172
|
-
TokenKind["CURRENT"] = "
|
|
1176
|
+
TokenKind["CURRENT"] = "TOKEN_CURRENT_VALUE";
|
|
1177
|
+
TokenKind["CURRENT_KEY"] = "TOKEN_CURRENT_KEY";
|
|
1173
1178
|
TokenKind["DDOT"] = "TOKEN_DDOT";
|
|
1174
1179
|
TokenKind["DOT"] = "TOKEN_DOT";
|
|
1175
1180
|
TokenKind["DOUBLE_QUOTE_STRING"] = "TOKEN_DOUBLE_QUOTE_STRING";
|
|
@@ -1183,7 +1188,10 @@ let TokenKind = /*#__PURE__*/function (TokenKind) {
|
|
|
1183
1188
|
TokenKind["GT"] = "TOKEN_GT";
|
|
1184
1189
|
TokenKind["INDEX"] = "TOKEN_INDEX";
|
|
1185
1190
|
TokenKind["KEY"] = "TOKEN_KEY";
|
|
1191
|
+
TokenKind["KEY_DOUBLE_QUOTE_STRING"] = "TOKEN_KEY_DOUBLE_QUOTE_STRING";
|
|
1192
|
+
TokenKind["KEY_SINGLE_QUOTE_STRING"] = "TOKEN_KEY_SINGLE_QUOTE_STRING";
|
|
1186
1193
|
TokenKind["KEYS"] = "TOKEN_KEYS";
|
|
1194
|
+
TokenKind["KEYS_FILTER"] = "TOKEN_KEYS_FILTER";
|
|
1187
1195
|
TokenKind["LBRACKET"] = "TOKEN_LBRACKET";
|
|
1188
1196
|
TokenKind["LE"] = "TOKEN_LE";
|
|
1189
1197
|
TokenKind["LG"] = "TOKEN_LG";
|
|
@@ -1264,6 +1272,7 @@ const indexPattern = /-?\d+/y;
|
|
|
1264
1272
|
const intPattern = /-?[0-9]+/y;
|
|
1265
1273
|
const namePattern = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/y;
|
|
1266
1274
|
const whitespace = new Set([" ", "\n", "\t", "\r"]);
|
|
1275
|
+
const nameFirstPattern = /[\u0080-\uFFFFa-zA-Z_]/; // don't set sticky bit
|
|
1267
1276
|
|
|
1268
1277
|
/**
|
|
1269
1278
|
* JSONPath lexical scanner.
|
|
@@ -1336,6 +1345,11 @@ class Lexer {
|
|
|
1336
1345
|
if (ch) this.backup();
|
|
1337
1346
|
return ch;
|
|
1338
1347
|
}
|
|
1348
|
+
peekMatch(pattern) {
|
|
1349
|
+
const ch = this.next();
|
|
1350
|
+
if (ch) this.backup();
|
|
1351
|
+
return pattern.test(ch);
|
|
1352
|
+
}
|
|
1339
1353
|
accept(valid) {
|
|
1340
1354
|
const ch = this.next();
|
|
1341
1355
|
if (valid.has(ch)) return true;
|
|
@@ -1465,10 +1479,28 @@ function lexDescendantSelection(l) {
|
|
|
1465
1479
|
l.emit(TokenKind.NAME);
|
|
1466
1480
|
return lexSegment;
|
|
1467
1481
|
}
|
|
1468
|
-
if (!l.environment.strict
|
|
1469
|
-
//
|
|
1470
|
-
|
|
1471
|
-
|
|
1482
|
+
if (!l.environment.strict) {
|
|
1483
|
+
// We're effectively disabling the _key selector_ and _keys filter selector_ if a
|
|
1484
|
+
// custom _keys selector_ is set.
|
|
1485
|
+
if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
|
|
1486
|
+
l.next();
|
|
1487
|
+
if (l.peekMatch(nameFirstPattern)) {
|
|
1488
|
+
// Non-standard key selector
|
|
1489
|
+
l.ignore(); // ignore ~
|
|
1490
|
+
l.acceptMatchRun(namePattern);
|
|
1491
|
+
l.emit(TokenKind.KEY);
|
|
1492
|
+
return lexSegment;
|
|
1493
|
+
} else {
|
|
1494
|
+
// Non-standard keys selector
|
|
1495
|
+
l.emit(TokenKind.KEYS);
|
|
1496
|
+
return lexSegment;
|
|
1497
|
+
}
|
|
1498
|
+
} else if (l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1499
|
+
// NOTE: A custom keys pattern does not play well with other non-standard key selectors.
|
|
1500
|
+
// We leave this here for backwards compatibility.
|
|
1501
|
+
l.emit(TokenKind.KEYS);
|
|
1502
|
+
return lexSegment;
|
|
1503
|
+
}
|
|
1472
1504
|
}
|
|
1473
1505
|
const ch = l.next();
|
|
1474
1506
|
switch (ch) {
|
|
@@ -1493,9 +1525,28 @@ function lexDotSelector(l) {
|
|
|
1493
1525
|
l.error("unexpected whitespace after dot");
|
|
1494
1526
|
return null;
|
|
1495
1527
|
}
|
|
1496
|
-
if (!l.environment.strict
|
|
1497
|
-
|
|
1498
|
-
|
|
1528
|
+
if (!l.environment.strict) {
|
|
1529
|
+
// We're effectively disabling the _key selector_ and _keys filter selector_ if a
|
|
1530
|
+
// custom _keys selector_ is set.
|
|
1531
|
+
if (l.environment.keysPattern.source === "~" && l.peek() === "~") {
|
|
1532
|
+
l.next();
|
|
1533
|
+
if (l.peekMatch(nameFirstPattern)) {
|
|
1534
|
+
// Non-standard key selector
|
|
1535
|
+
l.ignore(); // ignore ~
|
|
1536
|
+
l.acceptMatchRun(namePattern);
|
|
1537
|
+
l.emit(TokenKind.KEY);
|
|
1538
|
+
return lexSegment;
|
|
1539
|
+
} else {
|
|
1540
|
+
// Non-standard keys selector
|
|
1541
|
+
l.emit(TokenKind.KEYS);
|
|
1542
|
+
return lexSegment;
|
|
1543
|
+
}
|
|
1544
|
+
} else if (l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1545
|
+
// NOTE: A custom keys pattern does not play well with other non-standard key selectors.
|
|
1546
|
+
// We leave this here for backwards compatibility.
|
|
1547
|
+
l.emit(TokenKind.KEYS);
|
|
1548
|
+
return lexSegment;
|
|
1549
|
+
}
|
|
1499
1550
|
}
|
|
1500
1551
|
if (l.acceptMatchRun(namePattern)) {
|
|
1501
1552
|
l.emit(TokenKind.NAME);
|
|
@@ -1518,8 +1569,25 @@ function lexInsideBracketedSelection(l) {
|
|
|
1518
1569
|
continue;
|
|
1519
1570
|
}
|
|
1520
1571
|
if (!l.environment.strict && l.acceptMatchRun(l.environment.keysPattern)) {
|
|
1521
|
-
l.
|
|
1522
|
-
|
|
1572
|
+
switch (l.peek()) {
|
|
1573
|
+
case "'":
|
|
1574
|
+
l.ignore(); // ~
|
|
1575
|
+
l.next();
|
|
1576
|
+
return lexSingleQuoteKeyString(l);
|
|
1577
|
+
case '"':
|
|
1578
|
+
l.ignore(); // ~
|
|
1579
|
+
l.next();
|
|
1580
|
+
return lexDoubleQuoteKeyString(l);
|
|
1581
|
+
case "?":
|
|
1582
|
+
l.ignore(); // ~
|
|
1583
|
+
l.next();
|
|
1584
|
+
l.emit(TokenKind.KEYS_FILTER);
|
|
1585
|
+
l.filterLevel += 1;
|
|
1586
|
+
return lexInsideFilter;
|
|
1587
|
+
default:
|
|
1588
|
+
l.emit(TokenKind.KEYS);
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1523
1591
|
}
|
|
1524
1592
|
const ch = l.next();
|
|
1525
1593
|
switch (ch) {
|
|
@@ -1606,7 +1674,7 @@ function lexInsideFilter(l) {
|
|
|
1606
1674
|
l.emit(TokenKind.CURRENT);
|
|
1607
1675
|
return lexSegment;
|
|
1608
1676
|
case "#":
|
|
1609
|
-
l.emit(TokenKind.
|
|
1677
|
+
l.emit(TokenKind.CURRENT_KEY);
|
|
1610
1678
|
return lexSegment;
|
|
1611
1679
|
case ".":
|
|
1612
1680
|
l.backup();
|
|
@@ -1706,8 +1774,7 @@ function lexInsideFilter(l) {
|
|
|
1706
1774
|
* @param state - The state function to return control to.
|
|
1707
1775
|
* @returns String tokenizing state function.
|
|
1708
1776
|
*/
|
|
1709
|
-
function makeLexString(quote, state) {
|
|
1710
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
1777
|
+
function makeLexString(quote, state, token_kind) {
|
|
1711
1778
|
function _lexString(l) {
|
|
1712
1779
|
l.ignore();
|
|
1713
1780
|
if (l.peek() === quote) {
|
|
@@ -1733,7 +1800,7 @@ function makeLexString(quote, state) {
|
|
|
1733
1800
|
}
|
|
1734
1801
|
if (ch === quote) {
|
|
1735
1802
|
l.backup();
|
|
1736
|
-
l.emit(
|
|
1803
|
+
l.emit(token_kind);
|
|
1737
1804
|
l.next();
|
|
1738
1805
|
l.ignore();
|
|
1739
1806
|
return state;
|
|
@@ -1742,10 +1809,12 @@ function makeLexString(quote, state) {
|
|
|
1742
1809
|
}
|
|
1743
1810
|
return _lexString;
|
|
1744
1811
|
}
|
|
1745
|
-
const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection);
|
|
1746
|
-
const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection);
|
|
1747
|
-
const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter);
|
|
1748
|
-
const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter);
|
|
1812
|
+
const lexSingleQuoteStringInsideBracketSelection = makeLexString("'", lexInsideBracketedSelection, TokenKind.SINGLE_QUOTE_STRING);
|
|
1813
|
+
const lexDoubleQuoteStringInsideBracketSelection = makeLexString('"', lexInsideBracketedSelection, TokenKind.DOUBLE_QUOTE_STRING);
|
|
1814
|
+
const lexSingleQuoteStringInsideFilterExpression = makeLexString("'", lexInsideFilter, TokenKind.SINGLE_QUOTE_STRING);
|
|
1815
|
+
const lexDoubleQuoteStringInsideFilterExpression = makeLexString('"', lexInsideFilter, TokenKind.DOUBLE_QUOTE_STRING);
|
|
1816
|
+
const lexSingleQuoteKeyString = makeLexString("'", lexInsideBracketedSelection, TokenKind.KEY_SINGLE_QUOTE_STRING);
|
|
1817
|
+
const lexDoubleQuoteKeyString = makeLexString('"', lexInsideBracketedSelection, TokenKind.KEY_DOUBLE_QUOTE_STRING);
|
|
1749
1818
|
|
|
1750
1819
|
/**
|
|
1751
1820
|
* Base class for all JSONPath segments and selectors.
|
|
@@ -1786,7 +1855,7 @@ class NameSelector extends JSONPathSelector {
|
|
|
1786
1855
|
resolve(nodes) {
|
|
1787
1856
|
const rv = [];
|
|
1788
1857
|
for (const node of nodes) {
|
|
1789
|
-
if (hasStringKey(node.value, this.name)) {
|
|
1858
|
+
if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
|
|
1790
1859
|
rv.push(new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root));
|
|
1791
1860
|
}
|
|
1792
1861
|
}
|
|
@@ -1794,7 +1863,7 @@ class NameSelector extends JSONPathSelector {
|
|
|
1794
1863
|
}
|
|
1795
1864
|
*lazyResolve(nodes) {
|
|
1796
1865
|
for (const node of nodes) {
|
|
1797
|
-
if (hasStringKey(node.value, this.name)) {
|
|
1866
|
+
if (!isArray(node.value) && hasStringKey(node.value, this.name)) {
|
|
1798
1867
|
yield new JSONPathNode(node.value[this.name], node.location.concat(this.name), node.root);
|
|
1799
1868
|
}
|
|
1800
1869
|
}
|
|
@@ -1870,7 +1939,7 @@ class SliceSelector extends JSONPathSelector {
|
|
|
1870
1939
|
*lazyResolve(nodes) {
|
|
1871
1940
|
for (const node of nodes) {
|
|
1872
1941
|
if (!isArray(node.value)) continue;
|
|
1873
|
-
for (const [i, value] of this.
|
|
1942
|
+
for (const [i, value] of this.lazySlice(node.value, this.start, this.stop, this.step)) {
|
|
1874
1943
|
yield new JSONPathNode(value, node.location.concat(i), node.root);
|
|
1875
1944
|
}
|
|
1876
1945
|
}
|
|
@@ -1933,6 +2002,45 @@ class SliceSelector extends JSONPathSelector {
|
|
|
1933
2002
|
}
|
|
1934
2003
|
return slicedArray;
|
|
1935
2004
|
}
|
|
2005
|
+
|
|
2006
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
2007
|
+
*lazySlice(arr, start, stop, step) {
|
|
2008
|
+
if (!arr.length) return;
|
|
2009
|
+
|
|
2010
|
+
// Handle negative and undefined start values
|
|
2011
|
+
if (start === undefined || start === null) {
|
|
2012
|
+
start = step && step < 0 ? arr.length - 1 : 0;
|
|
2013
|
+
} else if (start < 0) {
|
|
2014
|
+
start = Math.max(arr.length + start, 0);
|
|
2015
|
+
} else {
|
|
2016
|
+
start = Math.min(start, arr.length - 1);
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
// Handle negative and undefined stop values
|
|
2020
|
+
if (stop === undefined || stop === null) {
|
|
2021
|
+
stop = step && step < 0 ? -1 : arr.length;
|
|
2022
|
+
} else if (stop < 0) {
|
|
2023
|
+
stop = Math.max(arr.length + stop, -1);
|
|
2024
|
+
} else {
|
|
2025
|
+
stop = Math.min(stop, arr.length);
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
// Perform the slice
|
|
2029
|
+
if (step === undefined) {
|
|
2030
|
+
// Default to a step of 1
|
|
2031
|
+
for (let i = start; i < stop; i += 1) {
|
|
2032
|
+
yield [i, arr[i]];
|
|
2033
|
+
}
|
|
2034
|
+
} else if (step > 0) {
|
|
2035
|
+
for (let i = start; i < stop; i += step) {
|
|
2036
|
+
yield [i, arr[i]];
|
|
2037
|
+
}
|
|
2038
|
+
} else if (step < 0) {
|
|
2039
|
+
for (let i = start; i > stop; i += step) {
|
|
2040
|
+
yield [i, arr[i]];
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
1936
2044
|
}
|
|
1937
2045
|
class WildcardSelector extends JSONPathSelector {
|
|
1938
2046
|
constructor(environment, token) {
|
|
@@ -1999,8 +2107,6 @@ class RecursiveDescentSegment extends JSONPathSelector {
|
|
|
1999
2107
|
}
|
|
2000
2108
|
}
|
|
2001
2109
|
}
|
|
2002
|
-
|
|
2003
|
-
// console.log(JSON.stringify(rv.map((n: any) => n.value)));
|
|
2004
2110
|
return this.selector.resolve(rv);
|
|
2005
2111
|
}
|
|
2006
2112
|
*lazyResolve(nodes) {
|
|
@@ -2357,6 +2463,38 @@ class CurrentKey extends FilterExpression {
|
|
|
2357
2463
|
}
|
|
2358
2464
|
}
|
|
2359
2465
|
|
|
2466
|
+
class KeySelector extends JSONPathSelector {
|
|
2467
|
+
constructor(environment, token, key) {
|
|
2468
|
+
let shorthand = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
2469
|
+
super(environment, token);
|
|
2470
|
+
this.environment = environment;
|
|
2471
|
+
this.token = token;
|
|
2472
|
+
this.key = key;
|
|
2473
|
+
this.shorthand = shorthand;
|
|
2474
|
+
}
|
|
2475
|
+
resolve(nodes) {
|
|
2476
|
+
const rv = [];
|
|
2477
|
+
for (const node of nodes) {
|
|
2478
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2479
|
+
if (isObject(node.value) && hasStringKey(node.value, this.key)) {
|
|
2480
|
+
rv.push(new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root));
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
return rv;
|
|
2484
|
+
}
|
|
2485
|
+
*lazyResolve(nodes) {
|
|
2486
|
+
for (const node of nodes) {
|
|
2487
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2488
|
+
if (isObject(node.value) && hasStringKey(node.value, this.key)) {
|
|
2489
|
+
yield new JSONPathNode(this.key, node.location.concat(`${KEY_MARK}${this.key}`), node.root);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
toString() {
|
|
2494
|
+
return this.shorthand ? `[~'${this.key.replaceAll("'", "\\'")}']` : `~'${this.key.replaceAll("'", "\\'")}'`;
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2360
2498
|
/**
|
|
2361
2499
|
* Object property name selector or array index selector.
|
|
2362
2500
|
*/
|
|
@@ -2373,10 +2511,8 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2373
2511
|
for (const node of nodes) {
|
|
2374
2512
|
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2375
2513
|
if (isObject(node.value)) {
|
|
2376
|
-
let i = 0;
|
|
2377
2514
|
for (const [key, _] of this.environment.entries(node.value)) {
|
|
2378
|
-
rv.push(new JSONPathNode(key, node.location.concat(
|
|
2379
|
-
i++;
|
|
2515
|
+
rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
|
|
2380
2516
|
}
|
|
2381
2517
|
}
|
|
2382
2518
|
}
|
|
@@ -2386,10 +2522,8 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2386
2522
|
for (const node of nodes) {
|
|
2387
2523
|
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2388
2524
|
if (isObject(node.value)) {
|
|
2389
|
-
let i = 0;
|
|
2390
2525
|
for (const [key, _] of this.environment.entries(node.value)) {
|
|
2391
|
-
yield new JSONPathNode(key, node.location.concat(
|
|
2392
|
-
i++;
|
|
2526
|
+
yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
|
|
2393
2527
|
}
|
|
2394
2528
|
}
|
|
2395
2529
|
}
|
|
@@ -2398,6 +2532,56 @@ class KeysSelector extends JSONPathSelector {
|
|
|
2398
2532
|
return this.shorthand ? "[~]" : "~";
|
|
2399
2533
|
}
|
|
2400
2534
|
}
|
|
2535
|
+
class KeysFilterSelector extends JSONPathSelector {
|
|
2536
|
+
constructor(environment, token, expression) {
|
|
2537
|
+
super(environment, token);
|
|
2538
|
+
this.environment = environment;
|
|
2539
|
+
this.token = token;
|
|
2540
|
+
this.expression = expression;
|
|
2541
|
+
}
|
|
2542
|
+
resolve(nodes) {
|
|
2543
|
+
const rv = [];
|
|
2544
|
+
for (const node of nodes) {
|
|
2545
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2546
|
+
if (isObject(node.value)) {
|
|
2547
|
+
for (const [key, value] of this.environment.entries(node.value)) {
|
|
2548
|
+
const filterContext = {
|
|
2549
|
+
environment: this.environment,
|
|
2550
|
+
currentValue: value,
|
|
2551
|
+
rootValue: node.root,
|
|
2552
|
+
currentKey: key
|
|
2553
|
+
};
|
|
2554
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2555
|
+
rv.push(new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root));
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
return rv;
|
|
2561
|
+
}
|
|
2562
|
+
*lazyResolve(nodes) {
|
|
2563
|
+
for (const node of nodes) {
|
|
2564
|
+
if (node.value instanceof String || isArray(node.value)) continue;
|
|
2565
|
+
if (isObject(node.value)) {
|
|
2566
|
+
for (const [key, value] of this.environment.entries(node.value)) {
|
|
2567
|
+
const filterContext = {
|
|
2568
|
+
environment: this.environment,
|
|
2569
|
+
currentValue: value,
|
|
2570
|
+
rootValue: node.root,
|
|
2571
|
+
lazy: true,
|
|
2572
|
+
currentKey: key
|
|
2573
|
+
};
|
|
2574
|
+
if (this.expression.evaluate(filterContext)) {
|
|
2575
|
+
yield new JSONPathNode(key, node.location.concat(`${KEY_MARK}${key}`), node.root);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
toString() {
|
|
2582
|
+
return `~?${this.expression.toString()}`;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2401
2585
|
|
|
2402
2586
|
const PRECEDENCE_LOWEST = 1;
|
|
2403
2587
|
const PRECEDENCE_LOGICAL_OR = 4;
|
|
@@ -2414,7 +2598,7 @@ const COMPARISON_OPERATORS = new Set(["==", ">=", ">", "<=", "<", "!="]);
|
|
|
2414
2598
|
class Parser {
|
|
2415
2599
|
constructor(environment) {
|
|
2416
2600
|
this.environment = environment;
|
|
2417
|
-
this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.
|
|
2601
|
+
this.tokenMap = new Map([[TokenKind.FALSE, this.parseBoolean], [TokenKind.NUMBER, this.parseNumber], [TokenKind.LPAREN, this.parseGroupedExpression], [TokenKind.NOT, this.parsePrefixExpression], [TokenKind.NULL, this.parseNull], [TokenKind.ROOT, this.parseRootQuery], [TokenKind.CURRENT, this.parseRelativeQuery], [TokenKind.SINGLE_QUOTE_STRING, this.parseString], [TokenKind.DOUBLE_QUOTE_STRING, this.parseString], [TokenKind.TRUE, this.parseBoolean], [TokenKind.FUNCTION, this.parseFunction], [TokenKind.CURRENT_KEY, this.parseCurrentKey]]);
|
|
2418
2602
|
}
|
|
2419
2603
|
parse(stream) {
|
|
2420
2604
|
if (stream.current.kind === TokenKind.ROOT) stream.next();
|
|
@@ -2446,6 +2630,8 @@ class Parser {
|
|
|
2446
2630
|
return new NameSelector(this.environment, stream.current, stream.current.value, true);
|
|
2447
2631
|
case TokenKind.WILD:
|
|
2448
2632
|
return new WildcardSelector(this.environment, stream.current, true);
|
|
2633
|
+
case TokenKind.KEY:
|
|
2634
|
+
return new KeySelector(this.environment, stream.current, stream.current.value, true);
|
|
2449
2635
|
case TokenKind.KEYS:
|
|
2450
2636
|
return new KeysSelector(this.environment, stream.current, true);
|
|
2451
2637
|
case TokenKind.DDOT:
|
|
@@ -2541,6 +2727,13 @@ class Parser {
|
|
|
2541
2727
|
case TokenKind.WILD:
|
|
2542
2728
|
items.push(new WildcardSelector(this.environment, stream.current));
|
|
2543
2729
|
break;
|
|
2730
|
+
case TokenKind.KEY_SINGLE_QUOTE_STRING:
|
|
2731
|
+
case TokenKind.KEY_DOUBLE_QUOTE_STRING:
|
|
2732
|
+
items.push(new KeySelector(this.environment, stream.current, this.decodeString(stream.current, true), false));
|
|
2733
|
+
break;
|
|
2734
|
+
case TokenKind.KEYS_FILTER:
|
|
2735
|
+
items.push(this.parseFilter(stream, true));
|
|
2736
|
+
break;
|
|
2544
2737
|
case TokenKind.KEYS:
|
|
2545
2738
|
items.push(new KeysSelector(this.environment, stream.current));
|
|
2546
2739
|
break;
|
|
@@ -2561,6 +2754,7 @@ class Parser {
|
|
|
2561
2754
|
return new BracketedSelection(this.environment, token, items);
|
|
2562
2755
|
}
|
|
2563
2756
|
parseFilter(stream) {
|
|
2757
|
+
let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
2564
2758
|
const tok = stream.next();
|
|
2565
2759
|
const expr = this.parseFilterExpression(stream);
|
|
2566
2760
|
if (expr instanceof FunctionExtension) {
|
|
@@ -2569,7 +2763,7 @@ class Parser {
|
|
|
2569
2763
|
throw new JSONPathTypeError(`result of ${expr.name}() must be compared`, expr.token);
|
|
2570
2764
|
}
|
|
2571
2765
|
}
|
|
2572
|
-
return new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
|
|
2766
|
+
return keys ? new KeysFilterSelector(this.environment, tok, new LogicalExpression(tok, expr)) : new FilterSelector(this.environment, tok, new LogicalExpression(tok, expr));
|
|
2573
2767
|
}
|
|
2574
2768
|
parseBoolean(stream) {
|
|
2575
2769
|
if (stream.current.kind === TokenKind.FALSE) return new BooleanLiteral(stream.current, false);
|
|
@@ -2604,13 +2798,20 @@ class Parser {
|
|
|
2604
2798
|
return new InfixExpression(tok, left, operator, right);
|
|
2605
2799
|
}
|
|
2606
2800
|
parseGroupedExpression(stream) {
|
|
2607
|
-
stream.
|
|
2801
|
+
if (stream.peek.kind === TokenKind.RPAREN) {
|
|
2802
|
+
throw new JSONPathSyntaxError(`empty paren expression`, stream.current);
|
|
2803
|
+
}
|
|
2804
|
+
stream.next(); // eat open paren
|
|
2805
|
+
|
|
2608
2806
|
let expr = this.parseFilterExpression(stream);
|
|
2609
2807
|
stream.next();
|
|
2610
2808
|
while (stream.current.kind !== TokenKind.RPAREN) {
|
|
2611
2809
|
if (stream.current.kind === TokenKind.EOF) {
|
|
2612
2810
|
throw new JSONPathSyntaxError("unbalanced parentheses", stream.current);
|
|
2613
2811
|
}
|
|
2812
|
+
if (!BINARY_OPERATORS.has(stream.current.kind)) {
|
|
2813
|
+
throw new JSONPathSyntaxError(`expected an expression, found '${stream.current.value}'`, stream.current);
|
|
2814
|
+
}
|
|
2614
2815
|
expr = this.parseInfixExpression(stream, expr);
|
|
2615
2816
|
}
|
|
2616
2817
|
stream.expect(TokenKind.RPAREN);
|
|
@@ -2666,7 +2867,7 @@ class Parser {
|
|
|
2666
2867
|
msg = "end of expression";
|
|
2667
2868
|
break;
|
|
2668
2869
|
default:
|
|
2669
|
-
msg = `'${stream.current.value}`;
|
|
2870
|
+
msg = `'${stream.current.value}'`;
|
|
2670
2871
|
}
|
|
2671
2872
|
throw new JSONPathSyntaxError(`unexpected ${msg}`, stream.current);
|
|
2672
2873
|
}
|
|
@@ -2990,6 +3191,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
2990
3191
|
JSONPathRecursionLimitError: JSONPathRecursionLimitError,
|
|
2991
3192
|
JSONPathSyntaxError: JSONPathSyntaxError,
|
|
2992
3193
|
JSONPathTypeError: JSONPathTypeError,
|
|
3194
|
+
KEY_MARK: KEY_MARK,
|
|
2993
3195
|
Nothing: Nothing,
|
|
2994
3196
|
Token: Token,
|
|
2995
3197
|
TokenKind: TokenKind,
|
|
@@ -3482,7 +3684,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3482
3684
|
apply: apply
|
|
3483
3685
|
});
|
|
3484
3686
|
|
|
3485
|
-
const version = "1.
|
|
3687
|
+
const version = "1.3.0";
|
|
3486
3688
|
|
|
3487
3689
|
exports.DEFAULT_ENVIRONMENT = DEFAULT_ENVIRONMENT;
|
|
3488
3690
|
exports.FunctionExpressionType = FunctionExpressionType;
|