Last active
August 8, 2023 20:47
-
-
Save dfkaye/e1b7ee333a760003d4b9 to your computer and use it in GitHub Desktop.
JSONPath.js modified ~ replace eval() in filter() with operate() and compare() methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// STOLEN FROM GOOGLE CODE REPO 01 NOV 2014 | |
// MODIFIED FOR | |
// + 1 Nov 2104 - READABILITY | |
// + 3 NOV 2014 - use (content security policy) no-eval() approach, see filter() method | |
// + 10 Dec 2014 - revised filter() with switch | |
// + 23 Feb 2015 - revised operate() and compare() methods | |
// + 28 July 2015 - bug fix: `attr` var in filter() wasn't `var'd` | |
// + 9 May 2018 - show test output | |
/* JSONPath 0.8.5 - XPath for JSON | |
* | |
* Copyright (c) 2007 Stefan Goessner (goessner.net) | |
* Licensed under the MIT (MIT-LICENSE.txt) licence. | |
* | |
* Proposal of Chris Zyp goes into version 0.9.x | |
* Issue 7 resolved | |
*/ | |
function jsonPath(obj, expr, arg) { | |
var P = { | |
resultType: arg && arg.resultType || "VALUE", | |
result: [], | |
normalize: function(expr) { | |
var subx = []; | |
return expr.replace(/[\['](\??\(.*?\))[\]']|\['(.*?)'\]/g, | |
function ($0, $1, $2) { | |
return "[#" + (subx.push($1 || $2) -1) + "]"; | |
}) /* http://code.google.com/p/jsonpath/issues/detail?id=4 */ | |
.replace(/'?\.'?|\['?/g, ";") | |
.replace(/;;;|;;/g, ";..;") | |
.replace(/;$|'?\]|'$/g, "") | |
.replace(/#([0-9]+)/g, function ($0 , $1) { | |
return subx[$1]; | |
}); | |
}, | |
asPath: function(path) { | |
var x = path.split(";"), p = "$"; | |
for (var i = 1, n = x.length; i < n; i++) { | |
p += /^[0-9*]+$/.test(x[i]) ? ("[" + x[i] + "]") : ("['" + x[i] + "']"); | |
} | |
return p; | |
}, | |
store: function(p, v) { | |
if (p) { | |
P.result[P.result.length] = P.resultType == "PATH" ? P.asPath(p) : v; | |
} | |
return !!p; | |
}, | |
trace: function(expr, val, path) { | |
if (expr !== "") { | |
var x = expr.split(";"), loc = x.shift(); | |
x = x.join(";"); | |
if (val && val.hasOwnProperty(loc)) { | |
P.trace(x, val[loc], path + ";" + loc); | |
} | |
else if (loc === "*") { | |
P.walk(loc, x, val, path, function(m, l, x, v, p) { | |
P.trace(m + ";" + x, v, p); | |
}); | |
} | |
else if (loc === "..") { | |
P.trace(x, val, path); | |
P.walk(loc, x, val, path, function(m, l, x, v, p) { | |
typeof v[m] === "object" && P.trace("..;" + x, v[m], p + ";" + m); | |
}); | |
} | |
else if (/^\(.*?\)$/.test(loc)) {// [(expr)] | |
// P.trace(P.eval(loc, val, path.substr(path.lastIndexOf(";") + 1)) + | |
// ";" + x, val, path); | |
P.trace(P.filter(loc, val, path.substr(path.lastIndexOf(";") + 1)) + | |
";" + x, val, path); | |
} | |
else if (/^\?\(.*?\)$/.test(loc)) { // [?(expr)] | |
P.walk(loc, x, val, path, function (m, l, x, v, p) { | |
// if (P.eval(l.replace(/^\?\((.*?)\)$/, "$1"), | |
// v instanceof Array ? v[m] : v, m)) { | |
if (P.filter(l.replace(/^\?\((.*?)\)$/, "$1"), | |
v instanceof Array ? v[m] : v, m)) { | |
P.trace(m + ";" + x, v, p); | |
} | |
}); // issue 5 resolved | |
} | |
else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] phyton slice syntax | |
P.slice(loc, x, val, path); | |
} | |
else if (/,/.test(loc)) { // [name1,name2,...] | |
for (var s = loc.split(/'?,'?/), i = 0, n = s.length; i < n; i++) { | |
P.trace(s[i] + ";" + x, val, path); | |
} | |
} | |
} | |
else { | |
P.store(path, val); | |
} | |
}, | |
walk: function(loc, expr, val, path, f) { | |
if (val instanceof Array) { | |
for (var i = 0, n = val.length; i < n; i++) { | |
if (i in val) { | |
f(i, loc, expr, val, path); | |
} | |
} | |
} | |
else if (typeof val === "object") { | |
for (var m in val) { | |
if (val.hasOwnProperty(m)) { | |
f(m, loc, expr, val, path); | |
} | |
} | |
} | |
}, | |
slice: function(loc, expr, val, path) { | |
if (val instanceof Array) { | |
var len = val.length, start = 0, end = len, step = 1; | |
loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, | |
function ($0, $1, $2, $3) { | |
start = parseInt($1 || start, 10); | |
end = parseInt($2 || end, 10); | |
step = parseInt($3 || step, 10); | |
}); | |
start = (start < 0) ? Math.max(0, start + len) : Math.min(len, start); | |
end = (end < 0) ? Math.max(0, end + len) : Math.min(len, end); | |
for (var i = start; i < end; i += step) { | |
P.trace(i + ";" + expr, val, path); | |
} | |
} | |
}, | |
// eval: function(x, _v, _vname) { //console.warn(_vname); // _vname is index | |
filter: function(x, _v, _vname) { | |
// 01 NOV 2014: filter() replaces eval() for CSP | |
var s = x.replace(/(^|[^\\])@/g, "$1_v").replace(/\\@/g, "@"), | |
p = s.replace('_v.', ''), | |
test = false; | |
var op, comp, attr; | |
if ($ && _v) { | |
attr = /\([^\)]+\)$/.exec(p); | |
attr && (attr = attr[0].replace(/[\(\s\)]/g, '')); | |
// console.warn(!!attr); | |
if (attr && (op = /\-|\+|\*|\/|\%/.exec(attr))) { | |
test = operate(_v, op[0], attr); | |
} | |
else if (comp = /==|\<=|\>=|\<|\>/.exec(p)) { | |
test = compare(_v, comp[0], p); | |
} | |
else { | |
test = p in _v; | |
} | |
} | |
return test; | |
// try { | |
// return $ && _v && eval(s); | |
// } // issue 7 : resolved .. | |
// catch(e) { | |
// throw new SyntaxError("jsonPath: " + e.message + ": " + | |
// x.replace(/(^|[^\\])@/g, "$1_v") | |
// .replace(/\\@/g, "@")); | |
// } // issue 7 : resolved .. | |
} | |
}; | |
var operate = function(context, op, attr) { | |
// \-|\+|\*|\/|\% | |
var attrs = attr.split(op), | |
a = context[attrs[0]], | |
b = attrs[1]; | |
switch(op) { | |
case '-' : return a - b; | |
case '+' : return a + b; | |
case '*' : return a * b; | |
case '/' : return a / b; | |
case '%' : return a % b; | |
default : return false; | |
} | |
}; | |
var compare = function (context, comp, p) { | |
// ==|\<=|\>=|\<|\> | |
var attrs = p.replace(/\s/g, '').split(comp), | |
a = context[attrs[0]], | |
b = attrs[1]; | |
switch(comp) { | |
case '==' : return a == b; | |
case '<=' : return a <= b; | |
case '>=' : return a >= b; | |
case '<' : return a < b; | |
case '>' : return a > b; | |
default : return false; | |
} | |
}; | |
var $ = obj; | |
if (expr && obj && (P.resultType == "VALUE" || P.resultType == "PATH")) { | |
P.trace(P.normalize(expr).replace(/^\$;?/, ""), obj, "$"); // issue 6 resolved | |
return P.result.length ? P.result : false; | |
} | |
} | |
var data = { | |
"store": { | |
"book": [ | |
{ "category": "reference", | |
"author": "Nigel Rees", | |
"title": "Sayings of the Century", | |
"price": 8.95 | |
}, | |
{ "category": "fiction", | |
"author": "Evelyn Waugh", | |
"title": "Sword of Honour", | |
"price": 12.99 | |
}, | |
{ "category": "fiction", | |
"author": "Herman Melville", | |
"title": "Moby Dick", | |
"isbn": "0-553-21311-3", | |
"price": 8.99 | |
}, | |
{ "category": "fiction", | |
"author": "J. R. R. Tolkien", | |
"title": "The Lord of the Rings", | |
"isbn": "0-395-19395-8", | |
"price": 22.99 | |
} | |
], | |
"bicycle": { | |
"color": "red", | |
"price": 19.95 | |
} | |
} | |
}; | |
console.log(jsonPath(data, "$..author", {resultType:"PATH"})); | |
// [ | |
// "$['store']['book'][0]['author']", | |
// "$['store']['book'][1]['author']", | |
// "$['store']['book'][2]['author']", | |
// "$['store']['book'][3]['author']" | |
// ] | |
console.log(jsonPath(data, "$..book[?(@.isbn)]")); | |
// [ | |
// { author: "Herman Melville", category: "fiction", isbn: "0-553-21311-3", price: 8.99, title: "Moby Dick" }, | |
// { author: "J. R. R. Tolkien", category: "fiction", isbn: "0-395-19395-8", price: 22.99, title: "The Lord of the Rings" } | |
// ] | |
console.log(jsonPath(data, "$..book[(@.length - 2)]")); | |
// [ | |
// { author: "Herman Melville", category: "fiction", isbn: "0-553-21311-3", price: 8.99, title: "Moby Dick" } | |
// ] | |
console.log(jsonPath(data, "$..book[?(@.price > 12)]")); | |
// [ | |
// { author: "Evelyn Waugh", category: "fiction", price: 12.99, title: "Sword of Honour" }, | |
// { author: "J. R. R. Tolkien", category: "fiction", isbn: "0-395-19395-8", price: 22.99, title: "The Lord of the Rings" } | |
// ] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment