1 | JSPath [![Build Status](https://secure.travis-ci.org/dfilatov/jspath.png)](http://travis-ci.org/dfilatov/jspath)
|
2 | ============
|
3 |
|
4 | JSPath is a domain-specific language (DSL) that enables you to navigate and find data within your JSON documents. Using JSPath, you can select items of JSON in order to retrieve the data they contain.
|
5 |
|
6 | JSPath for JSON like an XPath for XML.
|
7 |
|
8 | It is heavily optimized both for Node.js and modern browsers.
|
9 |
|
10 | Table of Contents
|
11 | -----------------
|
12 | * [Getting Started](#getting-started)
|
13 | * [In the Node.js](#in-the-nodejs)
|
14 | * [In the Browsers](#in-the-browsers)
|
15 | * [Usage](#usage)
|
16 | * [Quick example](#quick-example)
|
17 | * [Documentation](#documentation)
|
18 | * [Location path](#location-path)
|
19 | * [Predicates](#predicates)
|
20 | * [Object predicates](#object-predicates)
|
21 | * [Comparison operators](#comparison-operators)
|
22 | * [String comparison operators](#string-comparison-operators)
|
23 | * [Logical operators](#logical-operators)
|
24 | * [Arithmetic operators](#arithmetic-operators)
|
25 | * [Operator precedence](#operator-precedence)
|
26 | * [Positional predicates](#positional-predicates)
|
27 | * [Multiple predicates](#multiple-predicates)
|
28 | * [Substitutions](#substitutions)
|
29 | * [Result](#result)
|
30 |
|
31 | Getting Started
|
32 | ---------------
|
33 | ###In the Node.js###
|
34 | You can install using Node Package Manager (npm):
|
35 |
|
36 | npm install jspath
|
37 |
|
38 | ###In the Browsers###
|
39 | ```html
|
40 | <script type="text/javascript" src="jspath.min.js"></script>
|
41 | ```
|
42 | Also RequireJS module format supported.
|
43 |
|
44 | JSPath has been tested in IE6+, Mozilla Firefox 3+, Chrome 5+, Safari 5+, Opera 10+.
|
45 |
|
46 | Usage
|
47 | -----
|
48 | ```javascript
|
49 | JSPath.apply(path, json);
|
50 | // or
|
51 | JSPath.apply(path, json, substs);
|
52 | ```
|
53 | where:
|
54 | <table>
|
55 | <tr>
|
56 | <th></th>
|
57 | <th>type</th>
|
58 | <th>description</th>
|
59 | <tr>
|
60 | <td>path</td>
|
61 | <td>String</td>
|
62 | <td>path expression</td>
|
63 | </tr>
|
64 | <tr>
|
65 | <td>json</td>
|
66 | <td>any valid JSON</td>
|
67 | <td>input JSON document</td>
|
68 | </tr>
|
69 | <tr>
|
70 | <td>substs</td>
|
71 | <td>Object</td>
|
72 | <td>substitutions (optional)</td>
|
73 | </tr>
|
74 | </table>
|
75 |
|
76 | ###Quick example###
|
77 | ```javascript
|
78 | JSPath.apply(
|
79 | '.automobiles{.maker === "Honda" && .year > 2009}.model',
|
80 | {
|
81 | "automobiles" : [
|
82 | { "maker" : "Nissan", "model" : "Teana", "year" : 2011 },
|
83 | { "maker" : "Honda", "model" : "Jazz", "year" : 2010 },
|
84 | { "maker" : "Honda", "model" : "Civic", "year" : 2007 },
|
85 | { "maker" : "Toyota", "model" : "Yaris", "year" : 2008 },
|
86 | { "maker" : "Honda", "model" : "Accord", "year" : 2011 }
|
87 | ],
|
88 | "motorcycles" : [{ "maker" : "Honda", "model" : "ST1300", "year" : 2012 }]
|
89 | });
|
90 | ```
|
91 | Result will be:
|
92 | ```javascript
|
93 | ['Jazz', 'Accord']
|
94 | ```
|
95 |
|
96 | Documentation
|
97 | -------------
|
98 | JSPath expression consists of two type of top-level expressions: location path (required) and predicates (optional).
|
99 |
|
100 | ###Location path###
|
101 | To select a items in JSPath, you use a location path.
|
102 | A location path consists of one or more location steps.
|
103 | Every location step starts with dot (.) or two dots (..) depending on the item you're trying to select:
|
104 | * .property — locates property immediately descended from the context items
|
105 | * ..property — locates property deeply descended from the context items
|
106 | * . — locates the context items itself
|
107 |
|
108 | You can use the wildcard symbol (*) instead of exact name of property:
|
109 | * .* — locates all properties immediately descended from the context items
|
110 | * ..* — locates all properties deeply descended from the context items
|
111 |
|
112 | Also JSPath allow to join several properties:
|
113 | * (.property1 | .property2 | .propertyN) — locates property1, property2, propertyN immediately descended from the context items
|
114 | * or even (.property1 | .property2.property2_1.property2_1_1) — locates .property1, .property2.property2_1.property2_1_1 items
|
115 |
|
116 | Your location path can be absolute or relative.
|
117 | If location path starts with the root (^) you are using an absolute location path — your location path begins from the root items.
|
118 |
|
119 | Consider the following JSON:
|
120 | ```javascript
|
121 | var doc = {
|
122 | "books" : [
|
123 | {
|
124 | "id" : 1,
|
125 | "title" : "Clean Code",
|
126 | "author" : { "name" : "Robert C. Martin" },
|
127 | "price" : 17.96
|
128 | },
|
129 | {
|
130 | "id" : 2,
|
131 | "title" : "Maintainable JavaScript",
|
132 | "author" : { "name" : "Nicholas C. Zakas" },
|
133 | "price" : 10
|
134 | },
|
135 | {
|
136 | "id" : 3,
|
137 | "title" : "Agile Software Development",
|
138 | "author" : { "name" : "Robert C. Martin" },
|
139 | "price" : 20
|
140 | },
|
141 | {
|
142 | "id" : 4,
|
143 | "title" : "JavaScript: The Good Parts",
|
144 | "author" : { "name" : "Douglas Crockford" },
|
145 | "price" : 15.67
|
146 | }
|
147 | ]
|
148 | };
|
149 | ```
|
150 |
|
151 | ####Examples####
|
152 | ```javascript
|
153 | // find all books authors
|
154 | JSPath.apply('.books.author', doc);
|
155 | /* [{ name : 'Robert C. Martin' }, { name : 'Nicholas C. Zakas' }, { name : 'Robert C. Martin' }, { name : 'Douglas Crockford' }] */
|
156 |
|
157 | // find all books author names
|
158 | JSPath.apply('.books.author.name', doc);
|
159 | /* ['Robert C. Martin', 'Nicholas C. Zakas', 'Robert C. Martin', 'Douglas Crockford' ] */
|
160 |
|
161 | // find all names in books
|
162 | JSPath.apply('.books..name', doc);
|
163 | /* ['Robert C. Martin', 'Nicholas C. Zakas', 'Robert C. Martin', 'Douglas Crockford' ] */
|
164 | ```
|
165 |
|
166 | ###Predicates###
|
167 | An JSPath predicate allows you to write very specific rules about the items you'd like to select when constructing your expressions.
|
168 | Predicates are filters that restrict the items selected by an location path. There are two possible type of predicates: object and positional.
|
169 |
|
170 | ###Object predicates###
|
171 | Object predicates can be used in a path expression to filter a subset of items according to a boolean expressions working on a properties of each item.
|
172 | Object predicates are embedded in braces.
|
173 |
|
174 | Basic expressions in object predicates:
|
175 | * numeric literals (e.g. 1.23)
|
176 | * string literals (e.g. "John Gold")
|
177 | * boolean literals (true/false)
|
178 | * subpathes (e.g. .nestedProp.deeplyNestedProp)
|
179 |
|
180 | JSPath allows to use in predicate expressions following types of operators:
|
181 | * comparison operators
|
182 | * string comparison operators
|
183 | * logical operators
|
184 | * arithmetic operators
|
185 |
|
186 | ####Comparison operators####
|
187 |
|
188 | <table>
|
189 | <tr>
|
190 | <td>==</td>
|
191 | <td>Returns is true if both operands are equal</td>
|
192 | <td>.books{.id == "1"}</td>
|
193 | </tr>
|
194 | <tr>
|
195 | <td>===</td>
|
196 | <td>Returns true if both operands are strictly equal with no type conversion</td>
|
197 | <td>.books{.id === 1}</td>
|
198 | </tr>
|
199 | <tr>
|
200 | <td>!=</td>
|
201 | <td>Returns true if the operands are not equal</td>
|
202 | <td>.books{.id != "1"}</td>
|
203 | </tr>
|
204 | <tr>
|
205 | <td>!==</td>
|
206 | <td>Returns true if the operands are not equal and/or not of the same type</td>
|
207 | <td>.books{.id !== 1}</td>
|
208 | </tr>
|
209 | <tr>
|
210 | <td>></td>
|
211 | <td>Returns true if the left operand is greater than the right operand</td>
|
212 | <td>.books{.id > 1}</td>
|
213 | </tr>
|
214 | <tr>
|
215 | <td>>=</td>
|
216 | <td>Returns true if the left operand is greater than or equal to the right operand</td>
|
217 | <td>.books{.id >= 1}</td>
|
218 | </tr>
|
219 | <tr>
|
220 | <td><</td>
|
221 | <td>Returns true if the left operand is less than the right operand</td>
|
222 | <td>.books{.id < 1}</td>
|
223 | </tr>
|
224 | <tr>
|
225 | <td><=</td>
|
226 | <td>Returns true if the left operand is less than or equal to the right operand</td>
|
227 | <td>.books{.id <= 1}</td>
|
228 | </tr>
|
229 | </table>
|
230 |
|
231 | Comparison rules:
|
232 | * if both operands to be compared are arrays, then the comparison will be
|
233 | true if there is a element in the first array and a element in the
|
234 | second array such that the result of performing the comparison of the two elements is true
|
235 | * if one operand is array and another is not, then the comparison will be true if there is element in
|
236 | array such that the result of performing the comparison of element and another operand is true
|
237 | * primitives to be compared as usual javascript primitives
|
238 |
|
239 | If both operands are strings, also available additional comparison operators:
|
240 | ####String comparison operators####
|
241 | <table>
|
242 | <tr>
|
243 | <td>==</td>
|
244 | <td>Like an usual '==' but case insensitive</td>
|
245 | <td>.books{.title == "clean code"}</td>
|
246 | </tr>
|
247 | <tr>
|
248 | <td>^==</td>
|
249 | <td>Returns true if left operand value beginning with right operand value</td>
|
250 | <td>.books{.title ^== "Javascript"}</td>
|
251 | </tr>
|
252 | <tr>
|
253 | <td>^=</td>
|
254 | <td>Like a '^==' but case insensitive</td>
|
255 | <td>.books{.title ^= "javascript"}</td>
|
256 | </tr>
|
257 | <tr>
|
258 | <td>$==</td>
|
259 | <td>Returns true if left operand value ending with right operand value</td>
|
260 | <td>.books{.title $== "Javascript"}</td>
|
261 | </tr>
|
262 | <tr>
|
263 | <td>$=</td>
|
264 | <td>Like a '$==' but case insensitive</td>
|
265 | <td>.books{.title $= "javascript"}</td>
|
266 | </tr>
|
267 | <tr>
|
268 | <td>*==</td>
|
269 | <td>Returns true if left operand value contains right operand value</td>
|
270 | <td>.books{.title *== "Javascript"}</td>
|
271 | </tr>
|
272 | <tr>
|
273 | <td>*=</td>
|
274 | <td>Like a '*==' but case insensitive</td>
|
275 | <td>.books{.title *= "javascript"}</td>
|
276 | </tr>
|
277 | </table>
|
278 |
|
279 | ####Logical operators####
|
280 |
|
281 | <table>
|
282 | <tr>
|
283 | <td>&&</td>
|
284 | <td>Returns true if both operands are true</td>
|
285 | <td>.books{.price > 19 && .author.name === "Robert C. Martin"}</td>
|
286 | </tr>
|
287 | <tr>
|
288 | <td>||</td>
|
289 | <td>Returns true if either operand is true</td>
|
290 | <td>.books{.title === "Maintainable JavaScript" || .title === "Clean Code"}</td>
|
291 | </tr>
|
292 | <tr>
|
293 | <td>!</td>
|
294 | <td>Returns true if operand is false</td>
|
295 | <td>.books{!.title}</td>
|
296 | </tr>
|
297 | </table>
|
298 |
|
299 | Logical operators convert their operands to boolean values using next rules:
|
300 | * if operand is array (as you remember result of applying subpath also array):
|
301 | * if length of array greater than zero, result will be true
|
302 | * else result will be false
|
303 | * Casting with double NOT (!!) javascript operator used in any other cases.
|
304 |
|
305 | ####Arithmetic operators####
|
306 |
|
307 | <table>
|
308 | <tr>
|
309 | <td>+</td>
|
310 | <td>addition</td>
|
311 | </tr>
|
312 | <tr>
|
313 | <td>-</td>
|
314 | <td>subtraction</td>
|
315 | </tr>
|
316 | <tr>
|
317 | <td>*</td>
|
318 | <td>multiplication</td>
|
319 | </tr>
|
320 | <tr>
|
321 | <td>/</td>
|
322 | <td>division</td>
|
323 | </tr>
|
324 | <tr>
|
325 | <td>%</td>
|
326 | <td>modulus</td>
|
327 | </tr>
|
328 | </table>
|
329 |
|
330 | ####Operator precedence####
|
331 | <table>
|
332 | <tr>
|
333 | <td>1 (top)</td>
|
334 | <td>! -<sup>unary</sup></td>
|
335 | </tr>
|
336 | <tr>
|
337 | <td>2</td>
|
338 | <td>* / %</td>
|
339 | </tr>
|
340 | <tr>
|
341 | <td>3</td>
|
342 | <td>+ -<sup>binary</sup></td>
|
343 | </tr>
|
344 | <tr>
|
345 | <td>4</td>
|
346 | <td>< <= > >=</td>
|
347 | </tr>
|
348 | <tr>
|
349 | <td>5</td>
|
350 | <td>== === != !== ^= ^== $== $= *= *==</td>
|
351 | </tr>
|
352 | <tr>
|
353 | <td>6</td>
|
354 | <td>&&</td>
|
355 | </tr>
|
356 | <tr>
|
357 | <td>7</td>
|
358 | <td>||</td>
|
359 | </tr>
|
360 | </table>
|
361 |
|
362 | Parentheses are used to explicitly denote precedence by grouping parts of an expression that should be evaluated first.
|
363 |
|
364 | ####Examples####
|
365 | ```javascript
|
366 | // find all book titles whose author is Robert C. Martin
|
367 | JSPath.apply('.books{.author.name === "Robert C. Martin"}.title', doc);
|
368 | /* ['Clean Code', 'Agile Software Development'] */
|
369 |
|
370 | // find all book titles with price less than 17
|
371 | JSPath.apply('.books{.price < 17}.title', doc);
|
372 | /* ['Maintainable JavaScript', 'JavaScript: The Good Parts'] */
|
373 | ```
|
374 |
|
375 | ###Positional predicates###
|
376 | Positional predicates allows you to filter items by their context position.
|
377 | Positional predicates are always embedded in square brackets.
|
378 |
|
379 | There are four available forms:
|
380 | * [ _index_ ] — returns _index_-positioned item in context (first item is at index 0), e.g. [3] returns fourth item in context
|
381 | * [ _index_ : ] — returns items whose index in context is greater or equal to _index_, e.g. [2:] returns items whose index in context is greater or equal to 2
|
382 | * [ : _index_ ] — returns items whose index in context is smaller than _index_, e.g. [:5] returns first five items in context
|
383 | * [ _indexFrom_ : _indexTo_ ] — returns items whose index in context is greater or equal to _indexFrom_ and smaller than _indexTo_, e.g. [2:5] returns three items with indices 2, 3 and 4
|
384 |
|
385 | Also you can use negative position numbers:
|
386 | * [-1] — returns last item in context
|
387 | * [-3:] — returns last three items in context
|
388 |
|
389 | ####Examples####
|
390 | ```javascript
|
391 | // find first book title
|
392 | JSPath.apply('.books[0].title', doc);
|
393 | /* ['Clean Code'] */
|
394 |
|
395 | // find first title of books
|
396 | JSPath.apply('.books.title[0]', doc);
|
397 | /* 'Clean Code' */
|
398 |
|
399 | // find last book title
|
400 | JSPath.apply('.books[-1].title', doc);
|
401 | /* ['JavaScript: The Good Parts'] */
|
402 |
|
403 | // find two first book titles
|
404 | JSPath.apply('.books[:2].title', doc);
|
405 | /* ['Clean Code', 'Maintainable JavaScript'] */
|
406 |
|
407 | // find two last book titles
|
408 | JSPath.apply('.books[-2:].title', doc);
|
409 | /* ['Agile Software Development', 'JavaScript: The Good Parts'] */
|
410 |
|
411 | // find two book titles from second position
|
412 | JSPath.apply('.books[1:3].title', doc);
|
413 | /* ['Maintainable JavaScript', 'Agile Software Development'] */
|
414 | ```
|
415 |
|
416 | ###Multiple predicates###
|
417 | You can use more than one predicate. The result will contain only the items that match all the predicates.
|
418 |
|
419 | ####Examples####
|
420 | ```javascript
|
421 | // find first book name whose price less than 15 and greater than 5
|
422 | JSPath.apply('.books{.price < 15}{.price > 5}[0].title', doc);
|
423 | /* ['Maintainable JavaScript'] */
|
424 | ```
|
425 |
|
426 | ###Substitutions###
|
427 | Substitutions allows you to use a runtime-evaluated values in predicates.
|
428 |
|
429 | ####Examples####
|
430 | ```javascript
|
431 | var path = '.books{.author.name === $author}.title';
|
432 |
|
433 | // find book name whose author Nicholas C. Zakas
|
434 | JSPath.apply(path, doc, { author : 'Nicholas C. Zakas' });
|
435 | /* ['Maintainable JavaScript'] */
|
436 |
|
437 | // find books name whose authors Robert C. Martin or Douglas Crockford
|
438 | JSPath.apply(path, doc, { author : ['Robert C. Martin', 'Douglas Crockford'] });
|
439 | /* ['Clean Code', 'Agile Software Development', 'JavaScript: The Good Parts'] */
|
440 | ```
|
441 |
|
442 | ###Result###
|
443 | Result of applying JSPath is always an array (empty, if nothing found), excluding case when the last predicate in top-level expression is positional predicate with the exact index (e.g. [0], [5], [-1]).
|
444 | In this case, result is item at the specified index (accordingly _undefined_, if no item).
|