Giter Club home page Giter Club logo

jsonpath's Introduction

Build Status

jsonpath

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

Query Example

var cities = [
  { name: "London", "population": 8615246 },
  { name: "Berlin", "population": 3517424 },
  { name: "Madrid", "population": 3165235 },
  { name: "Rome",   "population": 2870528 }
];

var jp = require('jsonpath');
var names = jp.query(cities, '$..name');

// [ "London", "Berlin", "Madrid", "Rome" ]

Install

Install from npm:

$ npm install jsonpath

JSONPath Syntax

Here are syntax and examples adapted from Stefan Goessner's original post introducing JSONPath in 2007.

JSONPath Description
$ The root object/element
@ The current object/element
. Child member operator
.. Recursive descendant operator; JSONPath borrows this syntax from E4X
* Wildcard matching all objects/elements regardless their names
[] Subscript operator
[,] Union operator for alternate names or array indices as a set
[start:end:step] Array slice operator borrowed from ES4 / Python
?() Applies a filter (script) expression via static evaluation
() Script expression via static evaluation

Given this sample data set, see example expressions below:

{
  "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
    }
  }
}

Example JSONPath expressions:

JSONPath Description
$.store.book[*].author The authors of all books in the store
$..author All authors
$.store.* All things in store, which are some books and a red bicycle
$.store..price The price of everything in the store
$..book[2] The third book
$..book[(@.length-1)] The last book via script subscript
$..book[-1:] The last book via slice
$..book[0,1] The first two books via subscript union
$..book[:2] The first two books via subscript array slice
$..book[?(@.isbn)] Filter all books with isbn number
$..book[?(@.price<10)] Filter all books cheaper than 10
$..book[?(@.price==8.95)] Filter all books that cost 8.95
$..book[?(@.price<30 && @.category=="fiction")] Filter all fiction books cheaper than 30
$..* All members of JSON structure

Methods

jp.query(obj, pathExpression[, count])

Find elements in obj matching pathExpression. Returns an array of elements that satisfy the provided JSONPath expression, or an empty array if none were matched. Returns only first count elements if specified.

var authors = jp.query(data, '$..author');
// [ 'Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien' ]

jp.paths(obj, pathExpression[, count])

Find paths to elements in obj matching pathExpression. Returns an array of element paths that satisfy the provided JSONPath expression. Each path is itself an array of keys representing the location within obj of the matching element. Returns only first count paths if specified.

var paths = jp.paths(data, '$..author');
// [
//   ['$', 'store', 'book', 0, 'author'] },
//   ['$', 'store', 'book', 1, 'author'] },
//   ['$', 'store', 'book', 2, 'author'] },
//   ['$', 'store', 'book', 3, 'author'] }
// ]

jp.nodes(obj, pathExpression[, count])

Find elements and their corresponding paths in obj matching pathExpression. Returns an array of node objects where each node has a path containing an array of keys representing the location within obj, and a value pointing to the matched element. Returns only first count nodes if specified.

var nodes = jp.nodes(data, '$..author');
// [
//   { path: ['$', 'store', 'book', 0, 'author'], value: 'Nigel Rees' },
//   { path: ['$', 'store', 'book', 1, 'author'], value: 'Evelyn Waugh' },
//   { path: ['$', 'store', 'book', 2, 'author'], value: 'Herman Melville' },
//   { path: ['$', 'store', 'book', 3, 'author'], value: 'J. R. R. Tolkien' }
// ]

jp.value(obj, pathExpression[, newValue])

Returns the value of the first element matching pathExpression. If newValue is provided, sets the value of the first matching element and returns the new value.

jp.parent(obj, pathExpression)

Returns the parent of the first matching element.

jp.apply(obj, pathExpression, fn)

Runs the supplied function fn on each matching element, and replaces each matching element with the return value from the function. The function accepts the value of the matching element as its only parameter. Returns matching nodes with their updated values.

var nodes = jp.apply(data, '$..author', function(value) { return value.toUpperCase() });
// [
//   { path: ['$', 'store', 'book', 0, 'author'], value: 'NIGEL REES' },
//   { path: ['$', 'store', 'book', 1, 'author'], value: 'EVELYN WAUGH' },
//   { path: ['$', 'store', 'book', 2, 'author'], value: 'HERMAN MELVILLE' },
//   { path: ['$', 'store', 'book', 3, 'author'], value: 'J. R. R. TOLKIEN' }
// ]

jp.parse(pathExpression)

Parse the provided JSONPath expression into path components and their associated operations.

var path = jp.parse('$..author');
// [
//   { expression: { type: 'root', value: '$' } },
//   { expression: { type: 'identifier', value: 'author' }, operation: 'member', scope: 'descendant' }
// ]

jp.stringify(path)

Returns a path expression in string form, given a path. The supplied path may either be a flat array of keys, as returned by jp.nodes for example, or may alternatively be a fully parsed path expression in the form of an array of path components as returned by jp.parse.

var pathExpression = jp.stringify(['$', 'store', 'book', 0, 'author']);
// "$.store.book[0].author"

Differences from Original Implementation

This implementation aims to be compatible with Stefan Goessner's original implementation with a few notable exceptions described below.

Evaluating Script Expressions

Script expressions (i.e, (...) and ?(...)) are statically evaluated via static-eval rather than using the underlying script engine directly. That means both that the scope is limited to the instance variable (@), and only simple expressions (with no side effects) will be valid. So for example, ?(@.length>10) will be just fine to match arrays with more than ten elements, but ?(process.exit()) will not get evaluated since process would yield a ReferenceError. This method is even safer than vm.runInNewContext, since the script engine itself is more limited and entirely distinct from the one running the application code. See more details in the implementation of the evaluator.

Grammar

This project uses a formal BNF grammar to parse JSONPath expressions, an attempt at reverse-engineering the intent of the original implementation, which parses via a series of creative regular expressions. The original regex approach can sometimes be forgiving for better or for worse (e.g., $['store] => $['store']), and in other cases, can be just plain wrong (e.g. [ => $).

Other Minor Differences

As a result of using a real parser and static evaluation, there are some arguable bugs in the original library that have not been carried through here:

  • strings in subscripts may now be double-quoted
  • final step arguments in slice operators may now be negative
  • script expressions may now contain . and @ characters not referring to instance variables
  • subscripts no longer act as character slices on string elements
  • non-ascii non-word characters are no-longer valid in member identifier names; use quoted subscript strings instead (e.g., $['$'] instead of $.$)
  • unions now yield real unions with no duplicates rather than concatenated results

License

MIT

jsonpath's People

Contributors

alexandernst avatar chris-branch avatar dchester avatar horiuchi avatar ivangoncharov avatar matt- avatar oysteinamundsen avatar pmarrone avatar sdawood avatar toymachiner62 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonpath's Issues

Inverse operator

How can I reverse filter objects? Say, for example, the example data from the README of this repo. How can I filter all objects that don't have an isbn property?

I tried $..[?(@.isbn==undefined)] but that doesn't seem to be working.

Incorrect example

I noticed that this example is incorrect:

JSONPath Description
$..book[(@.length-1)] The third book via script subscript

It is not the third book - it is the last book. The third book would be:

JSONPath Description
$..book[(@.length-2)] The third book via script subscript

Filter nodes by array value

Given a JSON such as:

[
  {
     "type": ["a", "b"]
  },
  {
     "type": ["c"]
  }
]

I can't figure out how to get all nodes where type=='c'? Is there a 'contains value' operator or something like that?

Thanks.

Partial String Matching

Hi,
is there a way i can return nodes where a substring is matched.. like
$..[?(@.task=~/Cov.*/i)]
i have tried the above in various combinations but does not seem to return anything.

data:
[{'item': 'bar', 'group': 'cov001'},{'item': 'foo', 'group': 'cov002'},{'item': 'gee', 'group': 'cog001'}]

exclude fields

Hi, I'm going to build a part of my nodejs application using this module. I hope it fits my needs. I have a pretty big tree in json. If I query the first book $..book[0] I would like to exclude specific fields, for example the price. Like this result

               {
                    "category": "reference",
                    "author": "Nigel Rees",
                    "title": "Sayings of the Century"
                }

In XPath there are ways, how about here?

var db = {
        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
            }
        }
    };

jsonpath always return false or array?

when i have a data structure like
{
city1: ['BJ'],
city2: 'BJ'
}

when i use jsonpath to get the json value,
$.city1 it will return ['BJ']
$.city2 it will return ['BJ'] as well,
then the question come out, how could i juddge the element type is a String or is an Array?

Using jsonpath with r.js optimizer throwing error

Hi,

if I use jsonpath in the browser with require.js it works as expected. As soon as I run r.js optimizer it throwing the following error:
Mismatches anonymous define() module

I don't know why, but I have the strange felling that there is a error in the UMD. The require.js page states out, that this error can occur under the following situations: source

  • Be sure to load all scripts that call define() via the RequireJS API. Do not manually code script tags in HTML to load scripts that have define() calls in them.
    
  • If you manually code an HTML script tag, be sure it only includes named modules, and that an anonymous module that will have the same name as one of the modules in that file is not loaded.
    
  • If the problem is the use of loader plugins or anonymous modules but the RequireJS optimizer is not used for file bundling, use the RequireJS optimizer.
    
  • If the problem is the var define lint approach, use /*global define */ (no space before "global") comment style instead.
    

About the first 3 points I am pretty sure that they are not the case, because the lib is working without the optimizer. The last part I tried, but I can not find any documentation about the "global define" comment. Do you know, how I can use this lib in combination with the r.js optimizer?

Thanks and regards,
Jan

Infinite loop in slice if step arg is provided as string, or as integer > 1

infinite loop bug with when step arg is defined

  • If step is defined as a string value, which is the use case with the parser/handlers, step is never converted to integer, and loop index is coerced into string after i += step causing the loop termination condition to never fire
  • If step is converted into an integer and abs(step) > 1, the loop termination condition of i != end is again never fired

Add test to cover scenario: "$.store.book[0:4:2]"

[Proposal] jp.queryOne

jp.query is extremely helpful, but sometimes one needs (and expects) to find only 1 value (for example, when searching by ID). In those cases jp.query is wasting precious time iterating over all items, when it could return as soon as a match is found.

This could vastly improve performance whit big sets of data.

$..book[-1:] returns all the books

Last path selector returns all

const jsonpath = require('jsonpath');
const store = {
  "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.query(store, '$..book[-1:].title'));

All the books are pushed: [ 'Sayings of the Century', 'Sword of Honour', 'Moby Dick', 'The Lord of the Rings' ]

Advanced query into array

Hi,

Given a structure like this:

[
  {
    "id": 1,
    "tags": ["foo","bar"]
  },
  {
    "id": 2,
    "tags": ["bar","zot"]
  }
]

How can I do a selection that selects the whole object if the tag array contains "foo"?

This way I only get the tags-arrays back:

 '$..tags[?(@ === "foo")]'

My guess was:

'$..[?(@.tags[?(@property === "foo")])]'

But that gives me

Error in the given query: Lexical error on line 1. Unrecognized text.
...perty === "foo")])]
-----------------------^

This is what I want back from the query:

[
  {
    "id": 1,
    "tags": ["foo","bar"]
  }
]

Any help would be greatly appreciated.

Integer strings are not considered valid path members

Objects using string representations of integers as their keys are not correctly parsed. The integer key seems to be ignored as a valid path member. An example will make the issue clear:

const jp = require('jsonpath')

const library = {
  "books": {
    "1": {
      "author": "Herman Melville",
      "title": "Moby Dick"
    },
    "2": {
      "author": "J. R. R. Tolkien",
      "title": "The Lord of the Rings"
    }
  }
}

jp.query(library, '$.books.1.author')
// returns []

// but
const letterLibrary = {
  "books": {
    "a": {
      "author": "Herman Melville",
      "title": "Moby Dick"
    },
    "b": {
      "author": "J. R. R. Tolkien",
      "title": "The Lord of the Rings"
    }
  }
}

jp.query(letterLibrary, '$.books.a.author')
// returns [ 'Herman Melville' ]

Support for subscript-descendant-string_literal in handlers

Hi
There is a handler for member-descendant-identifier which helps resolve queries like $..author, which shows all paths where the author key exists from the root node, https://github.com/dchester/jsonpath/blob/master/lib/handlers.js#L45. But if we put the author in quotes, $..['author'], it has no handler (subscript-descendant-string_literal) to resolve it, though the handler is similar. My example is of course very simple, and there was probably a good reason why this is not supported. Just curious to know what that is.

Thanks

Templating using JSONPath expressions

This is more of a feature request.
I am trying to achieve the following:

var template = {
  message: 'Hello {{$.name}}! You have {{$.messages.length}} new messages.'
};

var data = {
  name: 'Alice',
  messages: [{/*...*/}, {/*...*/}, {/*...*/}]
};

var renderedMessage = jp.render(template, data);
// { "message": "Hello Alice! You have 3 new messages." }

The {{ }} delimitors and the method name jp.render are of course for illustration purposes.
Would this kind of usage be within the scope of this library?

value method not work with filters if property does not exist

Setting a unexisting property with "value" method not work if the path points to an array element using a filter.

Object:

{
   "list": [
       {
           "index": 0
       }
   ]
}
jsonpath.value(obj, "$.list[0].value", "test");

test

jsonpath.value(obj, "$.list[?(@.index == 0)].value", "test"); 

undefined

path is undefined

running this query throws path is undefined error:

'$..["prop1", "prop2"]'

if prop2 does not exist anywhere in the object being queried

Identifier may contain dashes

Current identifier string contains

identifier: "[a-zA-Z_]+[a-zA-Z0-9_]*"

which leads to

$.status.dev-01
-------------^
Expecting 'DOT', 'DOT_DOT', '[', got 'INTEGER'

which is wrong as long as "dev-01" key is valid.

Does path need to start with '$'?

const x = {a: 1};
jp.query(x, 'a')
// returns 1

jp.value(x, 'a', 2);
// returns 2, sets key 'a' to 2 in x

jp.value(x, 'b', 2);
// Error: couldn't understand path

jp.query(x, 'b')
// returns []

jp.value(x, '$..b', 2);
// returns 2 and sets key 'b' to 2 in x

I would prefer to be able to leave the '$' off -- which mostly works -- as it seems redundant. But it seems to be necessary using "value" if the location does not yet exist.

Please update jison dependency (problem shrinkwrap with current)

The current jison being required has itself a broken nomnom (0.4.3) requirement that breaks shrinkwrap:

npm install jsonpath --save
npm shrinkwrap

npm ERR! Error: Problems were encountered
npm ERR! Please correct and try again.
npm ERR! missing: findit@>=0.0.3 <0.1, required by [email protected]
npm ERR! missing: source@>=0.0.3 <0.1, required by [email protected]
npm ERR! missing: es5-shim@>=1.0.0, required by [email protected]
npm ERR! missing: coffee-script@>=1.1.1 <1.2, required by [email protected]
npm ERR! missing: hashish@>=0.0.2, required by [email protected]

This is because nomnom erroneously includes a copy of browserify (but doesn't put it in the package).

Setting value using 'value'-method doesn't work

'value'-method does not set a new value:

jsonpath.value({
   "list": [
       {
         "index": 0,
         "value": "default"
       },
       {
         "index": 1,
         "value": "default"
       }
   ]
}, "$.list[?(@.index == 1)].value", "test");

'default'

Invalid calling object in IE 11 - windows 10 users

Hi
I have used this plugin for json traverse. Getting invalid calling object error in IE11 alone.
Found this line causing error
function _is_string(obj) {
return toString.call(obj) == '[object String]';
}

Fixed by changing code like this

function _is_string(obj) {
var toString = Object.prototype.toString;
return toString.call(obj) == '[object String]';
}

Please verify from your end. Maybe it will help others

Quesiton about consistency

Given this object:

{
  "store": {
    "books": [
      {
        "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
    }
  }
}

I would expect that these would be equivalent:
jsonpath.query(jsObj, '$.store.books[2]')
jsonpath.query(jsObj, '$.store.books')[2]

It turns out these are not equivalent; the first returns [{book2data}] and the second returns undefined. I was expecting to get {book2data} from both. It seems like the wrapping array creates the difference and I'm a bit confused as to what the expected behavior is.

Thanks for your thoughts!

resolve.require in grammar.js when using with jspm/systemjs

Hi - firstly, thanks for the work on this - it looks like it will be a very useful library for us.

I am trying to get it running with jspm - which like browserify has issues with the require.resolve. I have sorted this for aesprim.js by just including the modified version of the library instead (not perfect but does the trick for now).

With grammar.js it does not appear that the moduleInclude: & actionInclude: parameters that use require.resolve are used anywhere currently - are these historical, or there for future use, or am I just overlooking their purpose? Some basic testing seems to indicate that things work ok without these properties, but would be good to know what is likely to break without them.

Thanks
Glen

Error installing the module in Electron

I cannot install via npm , if i do that I get this error:

npm ERR! Darwin 14.5.0
npm ERR! argv "/usr/local/Cellar/node/5.5.0/bin/node" "/usr/local/bin/npm" "install" "jsonpath"
npm ERR! node v5.5.0
npm ERR! npm v3.5.3
npm ERR! code ELIFECYCLE

npm ERR! [email protected] postinstall: node lib/aesprim.js > generated/aesprim-browser.js
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] postinstall script 'node lib/aesprim.js > generated/aesprim-browser.js'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the jsonpath package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node lib/aesprim.js > generated/aesprim-browser.js
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs jsonpath
npm ERR! Or if that isn't available, you can get their info via:
npm ERR! npm owner ls jsonpath
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:

Path of '$' breaks for some methods

I’m not sure if this is intentional, but while a path of '$' — i.e. the root object — works fine with jp.query(), jp.paths(), jp.nodes(), jp.parse(), and jp.value() when not setting a new value, it causes an error for other methods. That makes sense for jp.parent() (although returning null or undefined might be friendlier than throwing an error), but it would be nice if jp.apply() and setting a new value with jp.value() worked too.

var jp = require('jsonpath')
let o = { foo: 'things' }
jp.value(o, '$')          // returns { foo: 'things' }
let newO = { bar: 'baz' }
jp.value(o, '$', newO)    // throws error “couldn’t understand path” in jp._normalize()

You can test this example on RunKit: https://runkit.com/embed/f3g7kw86i0z6

I realise that using jsonpath with a path of '$' is redundant, but wondered whether this behaviour was expected or not.

Can't use in Electron while packing with webpack

I'm coding an application that using Electron, and I'm also using webpack to compile my code.
But when using jsonpath, webpack shows a warning in console:

WARNING in ./~/jsonpath/lib/grammar.js
[0]     Critical dependencies:
[0]     4:0-7 require function is used in a way in which dependencies cannot be statically extracted
[0]     4:18-25 require function is used in a way in which dependencies cannot be statically extracted
[0]      @ ./~/jsonpath/lib/grammar.js 4:0-7 4:18-25

In Electron's console, there's an error:

fs.js:977Uncaught Error: EBADF: bad file descriptor, fstat
fs.fstatSync @ fs.js:977
tryStatSync @ fs.js:462
fs.readFileSync @ fs.js:510
fs.readFileSync @ ELECTRON_ASAR.js:500
(anonymous function) @ grammar.js:104
__webpack_require__ @ bootstrap 31da7ee…:531
fn @ bootstrap 31da7ee…:62
(anonymous function) @ parser.js:1
__webpack_require__ @ bootstrap 31da7ee…:531
fn @ bootstrap 31da7ee…:62
(anonymous function) @ index.js:3
__webpack_require__ @ bootstrap 31da7ee…:531
fn @ bootstrap 31da7ee…:62
(anonymous function) @ index.js:1
__webpack_require__ @ bootstrap 31da7ee…:531
fn @ bootstrap 31da7ee…:62
(anonymous function) @ index.js:12
__webpack_require__ @ bootstrap 31da7ee…:531
fn @ bootstrap 31da7ee…:62
(anonymous function) @ appContainer.js:8
__webpack_require__ @ bootstrap 31da7ee…:531
fn @ bootstrap 31da7ee…:62
(anonymous function) @ shell.js:13
__webpack_require__ @ bootstrap 31da7ee…:531
fn @ bootstrap 31da7ee…:62
(anonymous function) @ bootstrap 31da7ee…:554
__webpack_require__ @ bootstrap 31da7ee…:531
(anonymous function) @ bootstrap 31da7ee…:554
(anonymous function) @ bootstrap 31da7ee…:554

When I dig into it, here's the code that causing the error:

moduleInclude: fs.readFileSync(require.resolve("../include/module.js")),
actionInclude: fs.readFileSync(require.resolve("../include/action.js"))

It's in lib/grammar.js

Here's some code in my js which using jsonpath.

import jsonpath from 'jsonpath';

class Point {
...
jsonpath.query(this.object, this.path);
...
}

In webpack's config, target is set to electron, so it won't change require function, because Electron already offer one.

Error when jsonpath is loaded by webpack

I have been trying to integrate jsonpath into my webpack project, but I'm getting blocked the following error:

Uncaught ReferenceError: Invalid left-hand side in assignment

It appears the error is coming from the following line in grammar.js when it tries to stub out require.resolve:

require.resolve = require.resolve || function() {}

The above line of code is rewritten by webpack into this, which is invalid:

!(function webpackMissingModule() { var e = new Error("Cannot find module \".\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()).resolve = !(function webpackMissingModule() { var e = new Error("Cannot find module \".\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()).resolve || function() {}

For reference, I am using webpack v2.2.0-rc.3

Better syntax for filter expressions

Currently, we use this syntax to make filters:

$..book[?(@.price<30 && @.category=="fiction")]

I wonder if we can have a more concise syntax like this one:

$..book[@price<30 && @category=="fiction"]

It's more natural, right?

Document extra options for Browserify

Hi, I was trying to use this module as a dependency in another Node project, but when I ran Browserify in that project it resulted in the error Uncaught TypeError: require.resolve is not a function.

Eventually I found the additional options needed for jsonpath:

jsonpath/Gruntfile.js

Lines 16 to 26 in 855ddc0

require: [
/**
* When running in Node, we require('./aesprim') and that module takes care of monkey-patching esprima
* using resolve, path finding, etc...
* Anyways, Browserify doesn't support "resolve", so we need to trick the module. We'll actually be
* returning a verbatim, non-modified "esprima" when the code runs require('./aesprim').
* That is ok because we will modify the "esprima" source code right after the bundle process, via
* the postBundleCB callback.
*/
["esprima", {expose: "./aesprim"}]
],

jsonpath/Gruntfile.js

Lines 39 to 46 in 855ddc0

postBundleCB: function(err, src, next) {
/**
* This is ugly, but we need to make "esprima" understand '@' as a valid character.
* It's either this or bundle a copy of the library with those few bytes of changes.
*/
src = src.toString("utf8").replace(/(function isIdentifierStart\(ch\) {\s+return)/m, '$1 (ch == 0x40) || ');
next(err, new Buffer(src, "utf8"));
}

It would help to document this to prevent confusion for anyone else using jsonpath as a dependency in a Node project they plan to Browserify.

pre-bundled jsonpath.js crashes on $[a,b] selector: does not include _uniq from underscore

The pre-bundled jsonpath.js file included in the node package will crash when attempting to use an array selector operation due to missing underscore functions.

Webpack has given "underscore" module index 9, which in the bundle is:

9:[function(require,module,exports){
},{}]

As this exports no content, the line
var _uniq = require('underscore').uniq;
sets _uniq to undefined, causing the crash when unique() is called on line 4855.

After a parse fails, parse.yy.ast is carrying left overs from the partially successful parse.

Currently after a parse error, parse.yy.ast is carrying left overs from the partially successful parse.
If the next path string has an identical "correct" prefix or the next path string has the same number of components or more, the bug goes unnoticed.

Example:

test('parser ast is reinitialized after parse() throws', function() {
assert.throws(function() { var path = jp.parse('store.book...') })
var path = jp.parse('$..price');
assert.deepEqual(path, [
{ "expression": { "type": "root", "value": "$" } },
{ "expression": { "type": "identifier", "value": "price" }, "operation": "member", "scope": "descendant"}
])
});

// Notice left-over component: { expression: { type: 'identifier', value: 'book' }, ... }

  1. parse-negative parser ast is reinitialized after parse() throws:
    AssertionError: [ { expression: { type: 'root', value: '$' } },
    { expression: { type: 'identifier', value: 'book' },
    scope: 'child',
    o deepEqual [ { expression: { type: 'root', value: '$' } },
    { expression: { type: 'identifier', value: 'price' },
    operation: 'member',

Querying using Union subscript with inexistent name identifiers causes errors.

Given the 'store' test .json file

{ "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
    }
  }
}

Using union for querying inexistent identifiers works fine for out of range indices
$.store.book[0,1,2,3,151]['title','author','price']

But throws an undefined variable error when one of the string identifiers doesn't exists
$.store.book[0,1,2,3]['title','author','price','fruit']

throws

     TypeError: Cannot read property 'path' of undefined
      at lib/handlers.js:248:27
      at _.uniq._.unique (node_modules/underscore/underscore.js:516:24)
      at unique (lib/handlers.js:246:10)
      at Handlers.subscript-child-union (lib/handlers.js:86:12)
      at lib/index.js:137:21
      at Array.forEach (native)
      at lib/index.js:134:14
      at Array.forEach (native)
      at JSONPath.nodes (lib/index.js:128:8)
      at Context.<anonymous> (test/query.js:273:22)

SUBSCRIPT_EXPRESSION_LIST grammar handles lists of two expressions only, with some edge cases

Hi,

I noticed that grammar tries to parse expression lists of arbitrary length, but it actually doesn't work for lists longer than two expressions

Grammar also permits lists of two script expressions or filter expressions or even '*' but handler doesn't support those actions, only handlers for INTEGER, STRING_LITERAL and ARRAY_SLICE work.

  1. Expression list longer than 2, allowed but blows up in handlers
    jp.query(store, '$..book[1,2,3]')
    TypeError: undefined is not a function
  2. Slice list longer than 2, allowed but blows up in handlers
    jp.query(store, '$..book[1:2,3:4,5:6]')
    TypeError: undefined is not a function
  3. Meaningless combination of expressions
    jp.query(store, '$..book[STAR,STAR]') is allowed while it shouldn't be
  4. Lists of script expressions and filter expressions are allowed by the grammar, but blow up in eval:
    jp.query(store, '$..book[(2/1),(4/2)]')
    Error: Line 1: Unexpected token )
    ...
    at Handlers._fns.subscript-child-script_expression (/.../jsonpath/lib/handlers.js:135:12)

How to use multiple search criteria?

How can I apply multiple search criteria?

In this example I'd like to get the name of the author where the type is participant and the primary is false.

var book = {
  authors: [
    {
      type: 'participant',
      primary: 'false',
      name: 'John'
    },
    {
      type: 'author',
      primary: 'true',
      name: 'Fred'
    }
  ]
}

I tried this but to no success

console.log('mars = ', jsonpath.value(book, '$.book.auhors[?(@.type=="participant"),(@.primary=="false")]'));

use of instanceof is incorrect

Hey,

I tried to use the library where the library was created in a different scope than the tested object. This can happen in situation where using different frames for the require of the library and the tested object. In such situations the library fails. I suggest changing "x instanceof Array" to "Array.isArray(x)" and "x instanceof Object" to "typeof x === 'object'

Bad error message on parsing '$...id'.

When parsing this erroneous path: '$...id' (and others like it), the result is not a friendly parse error, but a TypeError from somewhere within jsonpath.

> var jp = require('jsonpath')
> jp.parse('$...id')
TypeError: Cannot read property 'initialize' of undefined
    at Object.parser.yy.parseError (/Users/wasyl/Projects/chap/chaplain/node_modules/jsonpath/lib/parser.js:10:18)
    at Object.parse (/Users/wasyl/Projects/chap/chaplain/node_modules/jsonpath/generated/parser.js:224:22)
    at JSONPath.parse (/Users/wasyl/Projects/chap/chaplain/node_modules/jsonpath/lib/index.js:19:22)
...

Adding an if in that line checking if parser.yy.ast is present before calling initialize() on it seems to fix that, but I'm not 100% sure that's the right fix.

value returns new value?

value() is documented as returning the new value if one is passed in. I thought it was a doc error, so I checked, and that is indeed what it does. But wouldn't it be more useful (and arguably more typical for such a function) to return the old value?

"proerty:name" problem

If property name in the json includes a ":", it fails parsing, tried quoting and escaping

"d:Id": [
"xxxxxxx"
]

content[0].d:Id[0]
------------^
pecting 'DOT', 'DOT_DOT', '[', got 'ARRAY_SLICE'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.