@astronautlabs/jsonpath
Version:
Query JavaScript objects with JSONPath expressions. Robust / safe JSONPath engine for Node.js.
183 lines • 7.8 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONPath = void 0;
var handlers_1 = require("./handlers");
var tokens_1 = require("./tokens");
var parser_1 = require("./parser");
var assert_1 = require("./assert");
var JSONPath = /** @class */ (function () {
function JSONPath() {
}
JSONPath.parse = function (string) {
assert_1.assert.ok(typeof string === 'string', "we need a path");
return new parser_1.Parser().parse(string);
};
JSONPath.parent = function (obj, string) {
assert_1.assert.ok(obj instanceof Object, "obj needs to be an object");
assert_1.assert.ok(string, "we need a path");
var node = this.nodes(obj, string)[0];
var key = node.path.pop(); /* jshint unused:false */
return this.value(obj, node.path);
};
JSONPath.apply = function (obj, string, fn) {
assert_1.assert.ok(obj instanceof Object, "obj needs to be an object");
assert_1.assert.ok(string, "we need a path");
assert_1.assert.equal(typeof fn, "function", "fn needs to be function");
var nodes = this.nodes(obj, string).sort(function (a, b) {
// sort nodes so we apply from the bottom up
return b.path.length - a.path.length;
});
nodes.forEach(function (node) {
var key = node.path.pop();
var parent = this.value(obj, this.stringify(node.path));
var val = node.value = fn.call(obj, parent[key]);
parent[key] = val;
}, this);
return nodes;
};
JSONPath.value = function (obj, path, value) {
assert_1.assert.ok(obj instanceof Object, "obj needs to be an object");
assert_1.assert.ok(path, "we need a path");
if (value !== undefined) {
var node = this.nodes(obj, path).shift();
if (!node)
return this._vivify(obj, path, value);
var key = node.path.slice(-1).shift();
var parent = this.parent(obj, this.stringify(node.path));
parent[key] = value;
}
return this.query(obj, this.stringify(path), 1).shift();
};
JSONPath._vivify = function (obj, string, value) {
var self = this;
assert_1.assert.ok(obj instanceof Object, "obj needs to be an object");
assert_1.assert.ok(string, "we need a path");
var path = new parser_1.Parser().parse(string)
.map(function (component) { return component.expression.value; });
var setValue = function (path, value) {
var key = path.pop();
var node = self.value(obj, path);
if (!node) {
setValue(path.concat(), typeof key === 'string' ? {} : []);
node = self.value(obj, path);
}
node[key] = value;
};
setValue(path, value);
return this.query(obj, string)[0];
};
JSONPath.query = function (obj, string, count) {
assert_1.assert.ok(obj instanceof Object, "obj needs to be an object");
assert_1.assert.ok(typeof string === 'string', "we need a path");
var results = this.nodes(obj, string, count)
.map(function (r) { return r.value; });
return results;
};
JSONPath.paths = function (obj, string, count) {
assert_1.assert.ok(obj instanceof Object, "obj needs to be an object");
assert_1.assert.ok(string, "we need a path");
var results = this.nodes(obj, string, count)
.map(function (r) { return r.path; });
return results;
};
JSONPath.nodes = function (obj, string, count) {
assert_1.assert.ok(obj instanceof Object, "obj needs to be an object");
assert_1.assert.ok(string, "we need a path");
if (count === 0)
return [];
var path = new parser_1.Parser().parse(string);
var handlers = new handlers_1.Handlers();
var partials = [{ path: ['$'], value: obj }];
var matches = [];
if (path.length && path[0].expression.type == 'root')
path.shift();
if (!path.length)
return partials;
path.forEach(function (component, index) {
if (matches.length >= count)
return;
var handler = handlers.resolve(component);
var _partials = [];
partials.forEach(function (p) {
if (matches.length >= count)
return;
var results = handler(component, p, count);
if (index == path.length - 1) {
// if we're through the components we're done
matches = matches.concat(results || []);
}
else {
// otherwise accumulate and carry on through
_partials = _partials.concat(results || []);
}
});
partials = _partials;
});
return count ? matches.slice(0, count) : matches;
};
JSONPath.stringify = function (path) {
assert_1.assert.ok(path, "we need a path");
var string = '$';
var templates = {
'descendant-member': '..{{value}}',
'child-member': '.{{value}}',
'descendant-subscript': '..[{{value}}]',
'child-subscript': '[{{value}}]'
};
path = this._normalize(path);
path.forEach(function (component) {
if (component.expression.type == 'root')
return;
var key = [component.scope, component.operation].join('-');
var template = templates[key];
var value;
if (component.expression.type == 'string_literal') {
value = JSON.stringify(component.expression.value);
}
else {
value = component.expression.value;
}
if (!template)
throw new Error("couldn't find template " + key);
string += template.replace(/{{value}}/, value);
});
return string;
};
JSONPath._normalize = function (path) {
assert_1.assert.ok(path, "we need a path");
if (typeof path == "string") {
return new parser_1.Parser().parse(path);
}
else if (Array.isArray(path) && typeof path[0] == "string") {
var _path = [{ expression: { type: "root", value: "$" } }];
path.forEach(function (component, index) {
if (component == '$' && index === 0)
return;
if (typeof component == "string" && component.match("^" + tokens_1.TOKENS.identifier + "$")) {
_path.push({
operation: 'member',
scope: 'child',
expression: { value: component, type: 'identifier' }
});
}
else {
var type = typeof component == "number" ?
'numeric_literal' : 'string_literal';
_path.push({
operation: 'subscript',
scope: 'child',
expression: { value: component, type: type }
});
}
});
return _path;
}
else if (Array.isArray(path) && typeof path[0] == "object") {
return path;
}
throw new Error("couldn't understand path " + path);
};
return JSONPath;
}());
exports.JSONPath = JSONPath;
//# sourceMappingURL=jsonpath.js.map
;