UNPKG

@astronautlabs/jsonpath

Version:

Query JavaScript objects with JSONPath expressions. Robust / safe JSONPath engine for Node.js.

183 lines 7.8 kB
"use strict"; 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