UNPKG

9.14 kBMarkdownView Raw
1[![Build Status](https://travis-ci.org/dchester/jsonpath.png?branch=master)](https://travis-ci.org/dchester/jsonpath)
2
3# jsonpath
4
5Query JavaScript objects with JSONPath expressions. Robust / safe JSONPath engine for Node.js.
6
7
8## Query Example
9
10```javascript
11var cities = [
12 { name: "London", "population": 8615246 },
13 { name: "Berlin", "population": 3517424 },
14 { name: "Madrid", "population": 3165235 },
15 { name: "Rome", "population": 2870528 }
16];
17
18var jp = require('jsonpath');
19var names = jp.query(cities, '$..name');
20
21// [ "London", "Berlin", "Madrid", "Rome" ]
22```
23
24## Install
25
26Install from npm:
27```bash
28$ npm install jsonpath
29```
30
31## JSONPath Syntax
32
33Here are syntax and examples adapted from [Stefan Goessner's original post](http://goessner.net/articles/JsonPath/) introducing JSONPath in 2007.
34
35JSONPath | Description
36-----------------|------------
37`$` | The root object/element
38`@` | The current object/element
39`.` | Child member operator
40`..` | Recursive descendant operator; JSONPath borrows this syntax from E4X
41`*` | Wildcard matching all objects/elements regardless their names
42`[]` | Subscript operator
43`[,]` | Union operator for alternate names or array indices as a set
44`[start:end:step]` | Array slice operator borrowed from ES4 / Python
45`?()` | Applies a filter (script) expression via static evaluation
46`()` | Script expression via static evaluation
47
48Given this sample data set, see example expressions below:
49
50```javascript
51{
52 "store": {
53 "book": [
54 {
55 "category": "reference",
56 "author": "Nigel Rees",
57 "title": "Sayings of the Century",
58 "price": 8.95
59 }, {
60 "category": "fiction",
61 "author": "Evelyn Waugh",
62 "title": "Sword of Honour",
63 "price": 12.99
64 }, {
65 "category": "fiction",
66 "author": "Herman Melville",
67 "title": "Moby Dick",
68 "isbn": "0-553-21311-3",
69 "price": 8.99
70 }, {
71 "category": "fiction",
72 "author": "J. R. R. Tolkien",
73 "title": "The Lord of the Rings",
74 "isbn": "0-395-19395-8",
75 "price": 22.99
76 }
77 ],
78 "bicycle": {
79 "color": "red",
80 "price": 19.95
81 }
82 }
83}
84```
85
86Example JSONPath expressions:
87
88JSONPath | Description
89------------------------------|------------
90`$.store.book[*].author` | The authors of all books in the store
91`$..author` | All authors
92`$.store.*` | All things in store, which are some books and a red bicycle
93`$.store..price` | The price of everything in the store
94`$..book[2]` | The third book via array subscript
95`$..book[(@.length-1)]` | The third book via script subscript
96`$..book[-1:]` | The last book in order
97`$..book[0,1]` | The first two books via subscript union
98`$..book[:2]` | The first two books via subscript array slice
99`$..book[?(@.isbn)]` | Filter all books with isbn number
100`$..book[?(@.price<10)]` | Filter all books cheaper than 10
101`$..book[?(@.price==8.95)]` | Filter all books that cost 8.95
102`$..book[?(@.price<30 && @.category=="fiction")]` | Filter all fiction books cheaper than 30
103`$..*` | All members of JSON structure
104
105
106## Methods
107
108#### jp.query(obj, pathExpression[, count])
109
110Find 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.
111
112```javascript
113var authors = jp.query(data, '$..author');
114// [ 'Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien' ]
115```
116
117#### jp.paths(obj, pathExpression[, count])
118
119Find 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.
120
121
122```javascript
123var paths = jp.paths(data, '$..author');
124// [
125// ['$', 'store', 'book', 0, 'author'] },
126// ['$', 'store', 'book', 1, 'author'] },
127// ['$', 'store', 'book', 2, 'author'] },
128// ['$', 'store', 'book', 3, 'author'] }
129// ]
130```
131
132#### jp.nodes(obj, pathExpression[, count])
133
134Find 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.
135
136```javascript
137var nodes = jp.nodes(data, '$..author');
138// [
139// { path: ['$', 'store', 'book', 0, 'author'], value: 'Nigel Rees' },
140// { path: ['$', 'store', 'book', 1, 'author'], value: 'Evelyn Waugh' },
141// { path: ['$', 'store', 'book', 2, 'author'], value: 'Herman Melville' },
142// { path: ['$', 'store', 'book', 3, 'author'], value: 'J. R. R. Tolkien' }
143// ]
144```
145
146#### jp.value(obj, pathExpression[, newValue])
147
148Returns 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.
149
150#### jp.parent(obj, pathExpression)
151
152Returns the parent of the first matching element.
153
154#### jp.apply(obj, pathExpression, fn)
155
156Runs 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.
157
158
159```javascript
160var nodes = jp.apply(data, '$..author', function(value) { return value.toUpperCase() });
161// [
162// { path: ['$', 'store', 'book', 0, 'author'], value: 'NIGEL REES' },
163// { path: ['$', 'store', 'book', 1, 'author'], value: 'EVELYN WAUGH' },
164// { path: ['$', 'store', 'book', 2, 'author'], value: 'HERMAN MELVILLE' },
165// { path: ['$', 'store', 'book', 3, 'author'], value: 'J. R. R. TOLKIEN' }
166// ]
167```
168
169#### jp.parse(pathExpression)
170
171Parse the provided JSONPath expression into path components and their associated operations.
172
173```javascript
174var path = jp.parse('$..author');
175// [
176// { expression: { type: 'root', value: '$' } },
177// { expression: { type: 'identifier', value: 'author' }, operation: 'member', scope: 'descendant' }
178// ]
179```
180
181#### jp.stringify(path)
182
183Returns 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`.
184
185```javascript
186var pathExpression = jp.stringify(['$', 'store', 'book', 0, 'author']);
187// "$.store.book[0].author"
188```
189
190## Differences from Original Implementation
191
192This implementation aims to be compatible with Stefan Goessner's original implementation with a few notable exceptions described below.
193
194#### Evaluating Script Expressions
195
196Script expressions (i.e, `(...)` and `?(...)`) are statically evaluated via [static-eval](https://github.com/substack/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](https://github.com/substack/static-eval/blob/master/index.js) of the evaluator.
197
198#### Grammar
199
200This project uses a formal BNF [grammar](https://github.com/dchester/jsonpath/blob/master/lib/grammar.js) 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. `[` => `$`).
201
202#### Other Minor Differences
203
204As 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:
205
206- strings in subscripts may now be double-quoted
207- final `step` arguments in slice operators may now be negative
208- script expressions may now contain `.` and `@` characters not referring to instance variables
209- subscripts no longer act as character slices on string elements
210- non-ascii non-word characters are no-longer valid in member identifier names; use quoted subscript strings instead (e.g., `$['$']` instead of `$.$`)
211- unions now yield real unions with no duplicates rather than concatenated results
212
213## License
214
215MIT
216