Skip to content

Commit 49085dd

Browse files
authored
v0.9.10 README
1 parent 83223f3 commit 49085dd

File tree

1 file changed

+338
-2
lines changed

1 file changed

+338
-2
lines changed

README.md

Lines changed: 338 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,338 @@
1-
# jsonpath
2-
JSONPath in the vein of S. Goessner, with some syntax refinements, eforcement, and extensions.
1+
# brunerd JSONPath
2+
3+
Another take on the [JSONPath](https://goessner.net/articles/JsonPath/) query language by Stefan Goessner.
4+
The engine is purposefully written in ES5 for the broadest compatibility.
5+
The normalize engine has been reworked to parse a path expression into an array. Previously the path was expressed internally as a semi-colon delimited string, which meant keys with semi-colons would fail and also allowed for many invalid expressions to slip by.
6+
7+
Notable enhancements include:
8+
- Arrays can now be referenced with positive *and* negative integers
9+
- Slice now allows a negative step integer, script expressions and can now operate on arrays *and* strings
10+
- Property names can be referenced using Unicode `\u` escape sequences
11+
- Property names containing `;` and `]` are no longer inaccessible, no more gotchas
12+
- Path output for JSONPath with options for dot style property names and single or double quote bracket styles
13+
- Path output in RFC6901 JSON Pointer style
14+
- Unions can now contain any and all valid JSONPath expressions as well as mixed quoting styles
15+
16+
## JSONPath Syntax
17+
18+
JSONPath Expression | Description
19+
-|-
20+
`$` | The root of the object, all queries must begin with this
21+
`.key`| Child operator `.` references the property named `key` (property names observe [Javascript naming rules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors))
22+
`..key`| Recursive descent operator `..` references all properties name `key` within the object
23+
`.*` or `[*]`| Wildcard operator `*` matches all object property name or array indices
24+
`..*` or `..[*]`| Wildcard operator with recursive descent explodes the contents of your JSON quite well
25+
`()`| A script expression uses the returned value of the expression to as the property name or index
26+
`?()`| A filter expression interrogates all array/object members against the expression, descending into or returning the value of those that match
27+
`@` | Use inside a filter or script expression. `@` is substituted with the value of the current object, `@name` will match the current property name, `@.length` will reference the length of an array or string, `@.key` will reference the property "key" of the current object
28+
`[]`| Subscript/child operator; can contain quoted property names (`'key'`,`"key"`), numbers (negative or positive), filter and script expressions, `*` and `-` operators
29+
`[start:end:step]`| Array/string slice operator like Python's, all field are optional, start and end default to bounds, step can be negative
30+
`[,]`| Union operator `,` allows multiple quoted key names, array indices, slices, script/filter expressions, and `*` to be combined
31+
`[-]`| One _after_ the last element in an array, borrowed from JSON Pointer, used for JSON creation only (not retrieval)
32+
33+
## Example queries:
34+
Use with the sample store.json data found below...
35+
36+
JSONPath Query| Description
37+
-|-
38+
`$.*` | Contents of all top level elements, in this case "store" and "expensive"
39+
`$..*` | All members of the JSON structure, mercilessly exploded via recursive descent
40+
`$.store.*` | All things in store, which are some books and a red bicycle
41+
`$.store..price` | The price of everything in the store
42+
`$..author` | All authors using recursive descent and dot notation
43+
`$..['author']` | All authors using recursive descent and single quoted bracket notation
44+
`$..["author"]` | All authors using recursive descent and double quoted bracket notation
45+
`$.store.book[*].author` | All authors of all books in the store (ensures "bicycle" is not included)
46+
`$.store.book[*]["author","title"]` | All authors and titles of all books within an array via union
47+
`$.expensive` | What is considered expensive in this store?
48+
`$..book[?(@.price <= $["expensive"])]` | All books less than or equal to the 'expensive' property
49+
`$..book[?(@.price > $.expensive)]` | All books more than the 'expensive' property
50+
`$["store"]..price` | The price of everything in the store
51+
`$..book[?(@.comment)]` | Filter all books with a "comment" property containing data
52+
`$..book[?(@.comment !== undefined)]` | Filter all books with a "comment" property
53+
`$.store.book[?(@.comment_hidden)]` | Filter books with the "comment_hidden" property
54+
`$..book[2]` | The third book (zero based array)
55+
`$..book[-1]` | The last book via negative index
56+
`$..book[(@.length-1)]`| The last book via script expression subscript
57+
`$..book[(@.length/2)]`| The middle book via script expression subscript
58+
`$..book[-2]` | The next to last book only via negative index
59+
`$..book[-2:]` | The last two books via slice
60+
`$..book[::-1]` | All books in the array in reverse order via slice
61+
`$..book[:(@.length/2)]` | First half of books
62+
`$..book[(@.length/2):]` | Last half of the books
63+
`$..book[0,1]`| The first two books via subscript union
64+
`$..book[:2]` | The first two books via subscript array slice
65+
`$..book[?(@.isbn)]` | Filter all books with isbn number
66+
`$..book[?(@.price<10)]`| Filter all books less than 10
67+
`$..book[?(!(@.price<10))]`| Filter all books NOT less than 10
68+
`$..book[?(@.price>=10)]`| Filter all books greater than or equal to 10 (same as above)
69+
`$..book[?(@.price==8.95)]`| Filter all books that cost exactly 8.95
70+
`$..book[?(@.category=="fiction")]` | Filter all books with a category of "fiction" exactly (will not match "Fiction")
71+
`$..book[?(@.price < $.expensive && @.category=~/fiction/i)]` | Filter all books less than the root property of "expensive" with case-insensitive category of "fiction"
72+
`$..book[?(@.price < $.expensive && /fiction/i.test(@.category))]` | Same as above but with Javascript style regex used in the filter expression, supported but ugly
73+
`$..book[?(@.price < 10 \|\| @.category=~/humor/i)]` | Filter all books less than 10 OR with case-insensitive category containing "humor"
74+
`$..book[?(@.title =~/\u9053\u5fb7/)]` | Finds all books beginning with 道德 using Unicode escape sequences in a regex
75+
`$.store.emoji["\ud83e\udd13"]` | Look up a Unicode key name using Unicode escape sequences
76+
77+
### Sample data:
78+
Adapted and expanded from Stefan Goessner's [original post](http://goessner.net/articles/JsonPath/)
79+
<details><summary><b>store.json</b></summary>
80+
<p>
81+
82+
```javascript
83+
{
84+
"store": {
85+
"book": [
86+
{
87+
"category": "reference",
88+
"author": "Nigel Rees",
89+
"title": "Sayings of the Century",
90+
"price": 8.95,
91+
"comment":"",
92+
"comment_hidden": "\"A bird in hand is worth two in the bush\" is still an awkward phrase."
93+
},
94+
{
95+
"category": "reference/humor",
96+
"author": "Eric S. Raymond",
97+
"title": "New Hackers Dictionary, 3rd edition",
98+
"isbn":"0-262-68092-0",
99+
"price": 18.99,
100+
"comment":"If you are a \ud83e\udd13 you are sure to be amused"
101+
},
102+
{
103+
"category": "fiction",
104+
"author": "Evelyn Waugh",
105+
"title": "Sword of Honour",
106+
"price": 12.99,
107+
"comment":"Page after page of war stories. Fun.",
108+
"comment_hidden":"This book is likely cursed."
109+
},
110+
{
111+
"category": "Fiction",
112+
"author": "Herman Melville",
113+
"title": "Moby Dick",
114+
"isbn": "0-553-21311-3",
115+
"price": 8.99,
116+
"comment":"Before Jaws, there was Moby Dick",
117+
"comment_hidden":"Based on Mocha Dick, who received no royalties"
118+
},
119+
{
120+
"category": "fiction",
121+
"author": "J. R. R. Tolkien",
122+
"title": "The Lord of the Rings",
123+
"isbn": "0-395-19395-8",
124+
"price": 22.99,
125+
"comment":"Precious. My precious. Collectors edition.",
126+
"comment_hidden":"Cursed but totally worth it."
127+
},
128+
{
129+
"category":"philosophy",
130+
"author":"老子",
131+
"title":"道德经",
132+
"price":30,
133+
"comment":"The Tao Te Ching in Chinese"
134+
},
135+
{
136+
"category":"philosophy",
137+
"author":"老子",
138+
"title":"道德經",
139+
"price":80,
140+
"comment":"The Tao Te Ching in Chinese, original title"
141+
}
142+
],
143+
"bicycle": {
144+
"author":"Bikes don't have authors, silly",
145+
"color": "red",
146+
"price": 19.95,
147+
"comment":"A great bike for a kid!",
148+
"comment_hidden":"Cursed but shiny!"
149+
},
150+
"emoji":{
151+
"🤓":{
152+
"description":"smiling face with glasses",
153+
"description_alternate":"nerd"
154+
}
155+
}
156+
},
157+
"expensive": 20
158+
}
159+
```
160+
</details>
161+
162+
## Source Files
163+
164+
- [jsonpath.js](./jsonpath.js) The JSONPath engine.
165+
- [jsonpath.no_comment.js](./jsonpath.no_comment.js) Same as above but without comments.
166+
- [jsonpath.min.js](./jsonpath.min.js) The minified "one-liner" version (not obfuscated)
167+
168+
### Invoking the `jsonpath()` function:
169+
`jsonPath(obj, expr [, arg])`
170+
171+
Parameters:
172+
`obj` (Object|Array|String|Number|Boolean|null):
173+
Object representing the JSON structure
174+
175+
`expr` (String|Array):
176+
Either a JSONPath expression in string form or a pre-composed array representation of a JSONPath or JSON Pointer expression.
177+
178+
`arg` (Object|undefined):
179+
The `arg` object controls output, it can contain the following properties and values:
180+
181+
`resultType` value | Description
182+
-|-
183+
`VALUE`|the result is the matching values (default)
184+
`PATH`|path(s) matched by the query in bracket notation with double quotes
185+
`PATH_DOTTED`|path(s) matched by the query in dot notation where possible with double quoted bracket notation otherwise
186+
`PATH_JSONPOINTER`|path(s) matched by the query in [JSON Pointer (RFC6901)](https://tools.ietf.org/html/rfc6901) format
187+
188+
Properties that apply to `PATH` and `PATH_DOTTED` `resultType` output:
189+
190+
`singleQuoteKeys` value | Description
191+
-|-
192+
`true`|Use single quotes for bracket notation
193+
`false`|The default, uses double quotes for bracket notation
194+
195+
`escapeUnicode` value| Description
196+
-|-
197+
`true` | Uses Unicode `\u` escape sequences for all characters outside the Basic Latin Unicode block
198+
`false` | The default, characters `\u0000-\u001f` are *always* Unicode escaped, with exceptions of `\b` `\f` `\n` `\r` and `\t`
199+
200+
Example `arg` object:
201+
`{resultType:"PATH_DOTTED",singleQuoteKeys:true,escapeUnicode:true}`
202+
203+
Return value:
204+
Always an array. Empty or otherwise. The original implementation returns false for no matches.
205+
206+
### JSONPath `expr` internal representation:
207+
Within `jsonpath()` the `expr` string is parsed into an array containing strings, numbers, and objects (with a single property of "expression")
208+
209+
JSONPath example:
210+
`$[*][(@.length/2)]["key"][?(@.subKey == 'cool')][0]` this expression is converted internally to an array:
211+
`[{"expression":"*"},{"expression":"(@.length/2)"},"key",{"expression":"?(@.subKey == \"cool\")"},0]`
212+
213+
An array of this same format can be passed into `jsonpath()` directly.
214+
Since JSON Pointer is so easily parsed, this allows for jsonpath() to be given an `expr` array derived from JSON Pointer.
215+
216+
```javascript
217+
//split on /
218+
expr = expr.split('/')
219+
//throw out first entry
220+
expr.shift()
221+
//replace special symbols ~1 and ~0 (in this order) with the actual characters
222+
//convert string representations of numbers to Number types (for proper quoting in PATH output)
223+
expr = expr.map(function (f){
224+
return f.replace(/~1/g,"/").replace(/~0/g,"~") })
225+
.map(function(a){ return a === "" ? "" : isNaN(a) ? a : Number(a)})
226+
```
227+
228+
JSON Pointer example:
229+
`/0/key/sub/` is a JSON Pointer the JSONPath equivalent is: `$[0].key.sub[""]` (yes, property names with empty strings are allowed in JSON!)
230+
Using the above code `expr` will convert internally to: `[0,"key","sub",""]`
231+
232+
## brunerd JSONPath grammar
233+
Hat tip to [cburgmer](https://github.com/cburgmer) for providing the bones of the grammar declaration with [Proposal A](https://github.com/cburgmer/json-path-comparison/blob/master/proposals/Proposal_A/README.md) which I've adjusted for this implementation
234+
235+
Start
236+
::= "$" Operator*
237+
238+
Operator
239+
::= DotChild
240+
| BracketChildren
241+
| RecursiveDescentChildren
242+
243+
DotChild
244+
::= "." DotChildName
245+
| ".*"
246+
247+
DotChildName
248+
::= [^0-9 -#%-\/:-@\[-^`{-~][\w\d$]*
249+
250+
BracketChildren
251+
::= "[" ws BracketElements ws "]"
252+
253+
BracketElements
254+
::= BracketElement ws "," ws BracketElements
255+
| BracketElement
256+
257+
BracketElement
258+
::= Integer? ":" Integer? ":" NonZeroInteger?
259+
| Integer? ":" Integer?
260+
| BracketChild
261+
| "*"
262+
| "-"
263+
| "?(" FilterExpression ")"
264+
| "(" ScriptExpression ")"
265+
266+
RecursiveDescentChildren
267+
::= ".." DotChildName
268+
| "..*"
269+
| ".." BracketChildren
270+
271+
BracketChild
272+
::= "'" SingleQuotedString "'"
273+
| '"' DoubleQuotedString '"'
274+
| Integer
275+
276+
FilterExpression
277+
::= LogicalAnd
278+
| LogicalOr
279+
| HigherPrecedenceFilterExpression
280+
281+
ScriptExpression
282+
::= LogicalAnd
283+
| LogicalOr
284+
| HigherPrecedenceFilterExpression
285+
286+
LogicalAnd
287+
::= HigherPrecedenceFilterExpression ws "&&" ws LogicalAnd
288+
| HigherPrecedenceFilterExpression ws "&&" ws HigherPrecedenceFilterExpression
289+
290+
LogicalOr
291+
::= HigherPrecedenceFilterExpression ws "||" ws LogicalOr
292+
| HigherPrecedenceFilterExpression ws "||" ws HigherPrecedenceFilterExpression
293+
294+
HigherPrecedenceFilterExpression
295+
::= FilterValue ws ComparisonOperator ws FilterValue
296+
| UnaryFilterExpression
297+
298+
UnaryFilterExpression
299+
::= FilterValue
300+
| "!" ws UnaryFilterExpression
301+
| "(" ws FilterExpression ws ")"
302+
303+
ComparisonOperator
304+
::= "=="
305+
| "!="
306+
| "==="
307+
| "!=="
308+
| "=~"
309+
| "<="
310+
| ">="
311+
| "<"
312+
| ">"
313+
314+
FilterValue
315+
::= "@" ScalarOperator*
316+
| "$" ScalarOperator*
317+
| SimpleValue
318+
319+
ScalarOperator
320+
::= "." DotChildName
321+
| "[" ws BracketChild ws "]"
322+
323+
SimpleValue
324+
::= "'" SingleQuotedString "'"
325+
| '"' DoubleQuotedString '"'
326+
| '/' RegexSearch '/'
327+
| Number
328+
| "false"
329+
| "true"
330+
| "null"
331+
| undefined
332+
333+
## References
334+
[Stefan Gössner](https://goessner.net/articles/JsonPath/) and his initial work on the concept of JSONPath.
335+
336+
Christoph Burgmer's [Proposal A](https://github.com/cburgmer/json-path-comparison/blob/master/proposals/Proposal_A/README.md) and his [JSON Path comparison](https://cburgmer.github.io/json-path-comparison/) matrix
337+
A vast collection of open source JSONPath implementations with all their their varying behaviors.
338+

0 commit comments

Comments
 (0)