Skip to content

Commit b901692

Browse files
committed
JSONPath: Test presence/absence of property w/ quantifier extension
1 parent 049d852 commit b901692

1 file changed

Lines changed: 35 additions & 6 deletions

File tree

src/js/jsonpath.js

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,12 @@ export class JSONPath {
167167
#CURRENT = 2;
168168
#CHILDREN = 3;
169169
#DESCENDANTS = 4;
170+
#QUANTIFIER = 5;
170171
#reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/;
171172
#reExpr = /^\s*([!=^$*]=|[<>]=?)\s*(.+?)\]/;
172173
#reIndice = /^-?\d+/;
173174
#reRval = /^=([a-z]+)\((.+)\)$/;
175+
#reQuantifier = /^\{(\d+|\d+,\d+|\d+,|,\d+)\};\$/;
174176
#root;
175177
#compiled;
176178
#compile(query, i) {
@@ -206,9 +208,32 @@ export class JSONPath {
206208
}
207209
continue;
208210
}
209-
if ( c === 0x24 /* $ */ ) {
210-
steps.push({ mv: this.#ROOT });
211-
i += 1;
211+
if ( c === 0x3B /* ; */ ) {
212+
if ( query.startsWith(';$', i) === false ) { return; }
213+
steps.push(
214+
{ mv: this.#QUANTIFIER, min: 1, max: 1e6 },
215+
{ mv: this.#ROOT }
216+
);
217+
i += 2;
218+
mv = this.#UNDEFINED;
219+
continue;
220+
}
221+
if ( c === 0x7B /* { */ ) {
222+
const match = this.#reQuantifier.exec(query.slice(i));
223+
if ( match === null ) { return; }
224+
const comma = match[1].indexOf(',');
225+
let min, max;
226+
if ( comma === -1 ) {
227+
min = max = parseInt(match[1]);
228+
} else {
229+
min = parseInt(match[1].slice(0, comma)) || 0;
230+
max = parseInt(match[1].slice(comma+1)) || 1e6;
231+
}
232+
steps.push(
233+
{ mv: this.#QUANTIFIER, min, max },
234+
{ mv: this.#ROOT }
235+
);
236+
i += match[0].length;
212237
mv = this.#UNDEFINED;
213238
continue;
214239
}
@@ -261,11 +286,9 @@ export class JSONPath {
261286
#evaluate(steps, pathin) {
262287
let resultset = [];
263288
if ( Array.isArray(steps) === false ) { return resultset; }
264-
for ( let i = 0; i < steps.length; i++ ) {
265-
const step = steps[i];
289+
for ( const step of steps ) {
266290
switch ( step.mv ) {
267291
case this.#ROOT:
268-
if ( resultset.length === 0 && i !== 0 ) { break; }
269292
resultset = [ [ '$' ] ];
270293
break;
271294
case this.#CURRENT:
@@ -282,6 +305,12 @@ export class JSONPath {
282305
resultset = this.#getMatches(resultset, step);
283306
break;
284307
}
308+
case this.#QUANTIFIER: {
309+
const { length } = resultset;
310+
if ( length < step.min || length > step.max ) { return []; }
311+
resultset = [];
312+
break;
313+
}
285314
default:
286315
break;
287316
}

0 commit comments

Comments
 (0)