UNPKG

4.54 kBJavaScriptView Raw
1// deno-lint-ignore-file no-explicit-any no-prototype-builtins
2// Modernized version of Stefan Goessner's original JSON Path implementation.
3// Copyright (c) 2007 Stefan Goessner (goessner.net)
4// Licensed under the MIT license.
5// TODO: refactor to avoid string splitting/joining
6export function* trace(expr, val, path) {
7 if (expr) {
8 const [loc, ...rest] = expr.split(";");
9 const x = rest.join(";");
10 if (val !== null && typeof val === 'object' && loc in val) {
11 yield* trace(x, val[loc], path + ";" + loc);
12 }
13 else if (loc === "*") {
14 for (const [m, _l, v, p] of walk(loc, val, path)) {
15 yield* trace(m + ";" + x, v, p);
16 }
17 }
18 else if (loc === "..") {
19 yield* trace(x, val, path);
20 for (const [m, _l, v, p] of walk(loc, val, path)) {
21 if (typeof v[m] === "object")
22 yield* trace("..;" + x, v[m], p + ";" + m);
23 }
24 }
25 else if (/,/.test(loc)) { // [name1,name2,...]
26 for (let s = loc.split(/'?,'?/), i = 0, n = s.length; i < n; i++)
27 yield* trace(s[i] + ";" + x, val, path);
28 }
29 else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] slice syntax
30 yield* slice(loc, x, val, path);
31 }
32 }
33 else
34 yield [path, val];
35}
36function* slice(loc, expr, val, path) {
37 if (val instanceof Array) {
38 const len = val.length;
39 let start = 0, end = len, step = 1;
40 loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, (_$0, $1, $2, $3) => {
41 start = parseInt($1 || start);
42 end = parseInt($2 || end);
43 step = parseInt($3 || step);
44 return '';
45 });
46 start = (start < 0) ? Math.max(0, start + len) : Math.min(len, start);
47 end = (end < 0) ? Math.max(0, end + len) : Math.min(len, end);
48 for (let i = start; i < end; i += step)
49 yield* trace(i + ";" + expr, val, path);
50 }
51}
52function* walk(loc, val, path) {
53 if (val instanceof Array) {
54 for (let i = 0, n = val.length; i < n; i++)
55 if (i in val)
56 yield [i, loc, val, path];
57 }
58 else if (typeof val === "object") {
59 for (const m in val)
60 if (val.hasOwnProperty(m))
61 yield [m, loc, val, path];
62 }
63}
64export function normalize(expr) {
65 const subX = [];
66 if (!expr.startsWith('$'))
67 expr = '$' + expr;
68 return expr
69 .replace(/[\['](\??\(.*?\))[\]']/g, (_$0, $1) => { return "[#" + (subX.push($1) - 1) + "]"; })
70 .replace(/'?\.'?|\['?/g, ";")
71 .replace(/;;;|;;/g, ";..;")
72 .replace(/;$|'?\]|'$/g, "")
73 .replace(/#([0-9]+)/g, (_$0, $1) => { return subX[$1]; });
74}
75// FIXME: avoid repeated split/join/regex.test
76export function match(expr, path) {
77 if (expr && path) {
78 const [loc, ...restLoc] = expr.split(";");
79 const [val, ...restVal] = path.split(";");
80 const exprRest = restLoc.join(";");
81 const pathRest = restVal.join(';');
82 if (loc === val) {
83 return match(exprRest, pathRest);
84 }
85 else if (loc === "*") {
86 return match(exprRest, pathRest);
87 }
88 else if (loc === "..") {
89 return match(exprRest, path) || match("..;" + exprRest, pathRest);
90 }
91 else if (/,/.test(loc)) { // [name1,name2,...]
92 if (loc.split(/'?,'?/).some(v => v === val))
93 return match(exprRest, pathRest);
94 else
95 return false;
96 }
97 else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] slice syntax
98 let start = 0, end = Number.MAX_SAFE_INTEGER, step = 1;
99 loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, (_$0, $1, $2, $3) => {
100 start = parseInt($1 || start);
101 end = parseInt($2 || end);
102 step = parseInt($3 || step);
103 return '';
104 });
105 const idx = Number(val);
106 if (start < 0 || end < 0 || step < 0)
107 throw TypeError('Negative numbers not supported. Can\'t know length ahead of time when stream parsing');
108 if (idx >= start && idx < end && start + idx % step === 0)
109 return match(exprRest, pathRest);
110 else
111 return false;
112 }
113 }
114 else if (!expr && !path)
115 return true;
116 return false;
117}
118//# sourceMappingURL=json-path.js.map
\No newline at end of file