Non-eval version by erithmetic · Pull Request #4 · JSONPath-Plus/JSONPath · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions lib/environment.js
72 changes: 72 additions & 0 deletions lib/expression.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
var Expression = module.exports = {
execute: function(expression, object) {
if(/[>=<]/.test(expression))
return this.operate(object, expression, '>=<');
else if(/[-+*\/]/.test(expression))
return this.operate(object, expression, '-+*/');
else if(/\./.test(expression))
return this.getProperty(expression, object);
else
throw new SyntaxError("jsonPath: invalid query syntax: " + expression);
},

getProperty: function(expression, object) {
var parts = expression.match(/\.([^\s]+)/);
var properties = parts[1].split('.');

for(var i = 0, n = properties.length; i < n; i++) {
var rhs;
try {
rhs = JSON.parse('"' + properties[i] + '"');
}
catch(e) {
throw new SyntaxError("jsonPath: invalid property name: " + properties[1]);
}
if(object.hasOwnProperty(rhs))
object = object[rhs];
else
return false;
}

return object;
},

getValue: function(expression) {
try {
return JSON.parse(expression);
}
catch(e) {
throw new SyntaxError("jsonPath: invalid right-hand-side value: " + expression);
}
},

operate: function(object, expression, operators) {
var regex = new RegExp("([^\(\)" + operators + "]+)([" + operators + "]+)([^\(\)]+)");
var parts = expression.match(regex);
var lhs = this.getProperty(parts[1], object);
var rhs = this.getValue(parts[3]);

switch(parts[2]) {
case '==':
return lhs == rhs;
case '>=':
return lhs >= rhs;
case '<=':
return lhs <= rhs;
case '>':
return lhs > rhs;
case '<':
return lhs < rhs;
case '-':
return lhs - rhs;
case '+':
return lhs + rhs;
case '*':
return lhs * rhs;
case '/':
return lhs / rhs;
default:
throw new SyntaxError("jsonPath: invalid query syntax: " + expression);
}
},
};
121 changes: 25 additions & 96 deletions lib/jsonpath.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,25 @@
/* JSONPath 0.8.0 - XPath for JSON
*
* Copyright (c) 2007 Stefan Goessner (goessner.net)
* Licensed under the MIT (MIT-LICENSE.txt) licence.
*/

exports.eval = jsonPath;
var cache = {};
function jsonPath(obj, expr, arg) {
var P = {
resultType: arg && arg.resultType || "VALUE",
result: [],
normalize: function(expr) {
if(cache[expr]) {
return cache[expr];
}

var subx = [];
ret = expr.replace(/[\['](\??\(.*?\))[\]']/g, function($0,$1){return "[#"+(subx.push($1)-1)+"]";})
.replace(/'?\.'?|\['?/g, ";")
.replace(/;;;|;;/g, ";..;")
.replace(/;$|'?\]|'$/g, "")
.replace(/#([0-9]+)/g, function($0,$1){return subx[$1];});
cache[expr] = ret;
return ret;
},
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)) { // [name1,name2,...]
for (var s=loc.split(/'?,'?/),i=0,n=s.length; i<n; i++)
P.trace(s[i]+";"+x, val, path);
}
else if (/^\(.*?\)$/.test(loc)) // [(expr)]
P.trace(P.eval(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[m],m)) P.trace(m+";"+x,v,p); });
else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) // [start:end:step] phyton slice syntax
P.slice(loc, 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);end=parseInt($2||end);step=parseInt($3||step);});
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) {
try { return $ && _v && eval(x.replace(/@/g, "_v")); }
catch(e) { throw new SyntaxError("jsonPath: " + e.message + ": " + x.replace(/@/g, "_v").replace(/\^/g, "_a")); }
}
};

var $ = obj;
if (expr && obj && (P.resultType == "VALUE" || P.resultType == "PATH")) {
P.trace(P.normalize(expr).replace(/^\$;/,""), obj, "$");
return P.result.length ? P.result : false;
}
}
/* JSONPath 0.8.0 - XPath for JSON
*
* Copyright (c) 2007 Stefan Goessner (goessner.net)
* Licensed under the MIT (MIT-LICENSE.txt) licence.
*/

var Environment = require('./environment');

var JSONPath = module.exports = {
eval: function jsonPath(jsonObject, expression, options) {
if(!jsonObject)
throw 'You must provide a JSON object';
if(!expression)
throw 'An expression is required';
if(!options)
options = {};
if(!options.resultType)
options.resultType = 'VALUE';
if(options.resultType != "VALUE" && options.resultType != "PATH")
throw 'Invalid options, resultType must be "VALUE" or "PATH"';

var env = new Environment(jsonObject, expression, options);
return env.execute();
}
};
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
{
"author": "Stefan Goessner",
"name": "JSONPath",
"name": "dkastner-JSONPath",
"description": "A JS implementation of JSONPath",
"contributors": [
{ "name": "Stefan Goessner", "email": "subbu@subbu.org" },
{ "name": "Mike Brevoort", "email": "mike@brevoort.com" }
{ "name": "Mike Brevoort", "email": "mike@brevoort.com" },
{ "name": "Derek Kastner", "email": "dkastner@gmail.com" }
],
"version": "0.8.2",
"version": "0.9.2",
"repository": {
"type": "git",
"url": "git://github.com/s3u/JSONPath.git"
"url": "git://github.com/dkastner/JSONPath.git"
},
"main" : "./lib/jsonpath",
"dependencies": {},
"devDependencies": {
"nodeunit": "latest"
"vows": "latest"
}
}
17 changes: 17 additions & 0 deletions test/environment-test.js
Loading