Skip to content
This repository was archived by the owner on Feb 11, 2022. It is now read-only.

Commit 2612f7f

Browse files
committed
Initial commit
0 parents  commit 2612f7f

File tree

6 files changed

+2046
-0
lines changed

6 files changed

+2046
-0
lines changed

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2016 Frank Edelhaeuser
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a
6+
copy of this software and associated documentation files (the "Software"),
7+
to deal in the Software without restriction, including without limitation
8+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
9+
and/or sell copies of the Software, and to permit persons to whom the
10+
Software is furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included
13+
in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# lua-jsonpath
2+
3+
Query Lua data structures with JsonPath expressions. Robust and safe JsonPath engine for Lua.
4+
5+
This library implements Stefan Goessner's [JsonPath syntax](http://goessner.net/articles/JsonPath/) in Lua. Lua JsonPath is compatible with David Chester's [Javascript implementation](https://github.com/dchester/jsonpath).
6+
7+
The JsonPath Lua library was written from scratch by Frank Edelhaeuser. It's a pure Lua implementation based on a PEG grammer handled by Roberto Ierusalimschy's fabulous [LPeg pattern-matching library](http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html).
8+
9+
Some of this README and a subset of test cases were adopted from David Chester's [Javascript implementation](https://github.com/dchester/jsonpath) which is based on Stefan Goessner's [original work](http://goessner.net/articles/JsonPath/).
10+
11+
12+
## Query Example
13+
14+
```lua
15+
local cities = {
16+
{ name = 'London', population = 8615246 },
17+
{ name = 'Berlin', population = 3517424 },
18+
{ name = 'Madrid', population = 3165235 },
19+
{ name = 'Rome', population = 2870528 }
20+
}
21+
22+
local jp = require('jsonpath')
23+
local names = jp.query(cities, '$..name')
24+
-- { 'London', 'Berlin', 'Madrid', 'Rome' }
25+
```
26+
27+
28+
## Install
29+
30+
```
31+
$ luarocks install lua-jsonpath
32+
```
33+
34+
35+
## JsonPath Syntax
36+
37+
Here are syntax and examples adapted from [Stefan Goessner's original post](http://goessner.net/articles/JsonPath/) introducing JsonPath in 2007.
38+
39+
JsonPath | Description
40+
--------------------|------------
41+
`$` | The root object/element
42+
`@` | The current object/element
43+
`.` | Child member operator
44+
`..` | Recursive descendant operator; JsonPath borrows this syntax from E4X
45+
`*` | Wildcard matching all objects/elements regardless their names
46+
`[]` | Subscript operator
47+
`[,]` | Union operator for alternate names or array indices as a set
48+
`[start:end:step]` | Array slice operator borrowed from ES4 / Python
49+
`?()` | Applies a filter (script) expression via static evaluation
50+
`()` | Script expression via static evaluation
51+
52+
Given this sample data set, see example expressions below:
53+
54+
```lua
55+
{
56+
store = {
57+
bicycle = {
58+
color = 'red',
59+
price = 19.95
60+
},
61+
book = {
62+
{
63+
category = 'reference',
64+
author = 'Nigel Rees',
65+
title = 'Sayings of the Century',
66+
price = 8.95
67+
}, {
68+
category = 'fiction',
69+
author = 'Evelyn Waugh',
70+
title = 'Sword of Honour',
71+
price = 12.99
72+
}, {
73+
category = 'fiction',
74+
author = 'Herman Melville',
75+
title = 'Moby Dick',
76+
isbn = '0-553-21311-3',
77+
price = 8.99
78+
}, {
79+
category = 'fiction',
80+
author = 'J. R. R. Tolkien',
81+
title = 'The Lord of the Rings',
82+
isbn = '0-395-19395-8',
83+
price = 22.99
84+
}
85+
}
86+
}
87+
}
88+
```
89+
90+
Example JsonPath expressions:
91+
92+
JsonPath | Description
93+
--------------------------------|------------
94+
`$.store.book[*].author` | The authors of all books in the store
95+
`$..author` | All authors
96+
`$.store.*` | All things in store, which are some books and a red bicycle
97+
`$.store..price` | The price of everything in the store
98+
`$..book[2]` | The third book via array subscript
99+
`$..book[(@.length-1)]` | The third book via script subscript
100+
`$..book[-1:]` | The last book in order
101+
`$..book[-2:]` | The last two books in order
102+
`$..book[-2:-1]` | The second to last book in order
103+
`$..book[0,1]` | The first two books via subscript union
104+
`$..book[:2]` | The first two books via subscript array slice
105+
`$..book[?(@.isbn)]` | Filter all books with ISBN number
106+
`$..book[?(@.price<10)]` | Filter all books cheaper than 10
107+
`$..book[?(@.price==8.95)]` | Filter all books that cost 8.95
108+
`$..book[?(@.price<30 && @.category=="fiction")]` | Filter all fiction books cheaper than 30
109+
`$..*` | All members of Lua structure
110+
111+
112+
#### Indices
113+
114+
Lua JsonPath uses zero-based array indices, as does Javascript and the JSON notation. This decision has been made to be compatible with the original JsonPath implementation, even though Lua normally uses one-based indices. This convention only applies to JsonPath specifications. The Lua objects processed and returned by this library still use one-based indices.
115+
116+
117+
## Methods
118+
119+
#### jp.query(obj, pathExpression[, count])
120+
121+
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.
122+
123+
```lua
124+
local authors = jp.query(data, '$..author')
125+
-- { 'Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien' }
126+
```
127+
128+
129+
#### jp.value(obj, pathExpression)
130+
131+
Returns the value of the first element matching `pathExpression`.
132+
133+
```lua
134+
local author = jp.value(data, '$..author')
135+
-- 'Nigel Rees'
136+
```
137+
138+
139+
#### jp.paths(obj, pathExpression[, count])
140+
141+
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.
142+
143+
144+
```lua
145+
local paths = jp.paths(data, '$..author')
146+
-- {
147+
-- {'$', 'store', 'book', 0, 'author' },
148+
-- {'$', 'store', 'book', 1, 'author' },
149+
-- {'$', 'store', 'book', 2, 'author' },
150+
-- {'$', 'store', 'book', 3, 'author' }
151+
-- }
152+
```
153+
154+
155+
#### jp.nodes(obj, pathExpression[, count])
156+
157+
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.
158+
159+
```lua
160+
local nodes = jp.nodes(data, '$..author')
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+
170+
#### jp.parse(pathExpression)
171+
172+
Parse the provided JsonPath expression into path components and their associated operations.
173+
174+
```lua
175+
local path = jp.parse('$..author')
176+
-- {
177+
-- '$',
178+
-- '..',
179+
-- 'author'
180+
-- }
181+
```
182+
183+
184+
#### jp.grammer()
185+
186+
Provides the lua-jsonpath LPEG grammer for embedding in higher level LPEG grammers.
187+
188+
The abstract syntax tree matched for JsonPath elementes in a higher level LPEG grammer can then be supplied to `jp.nodes()`, `jp.paths()` or `jp.query()` instead of the string `pathExpression`.
189+
190+
```lua
191+
local lpeg = require('lpeg')
192+
local assignment = lpeg.C(lpeg.R'az') * lpeg.P'=' * lpeg.P'"' * jp.grammer() * lpeg.P'"'
193+
local var, ast = assignment:match('x="$..author"')
194+
-- var = 'x'
195+
local results = jp.query(data, ast)
196+
-- { 'Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien' }
197+
```
198+
199+
200+
## Differences from Stefan Goessner's Original Implementation
201+
202+
This implementation aims to be compatible with Stefan Goessner's original implementation with a few notable exceptions described below.
203+
204+
#### Evaluating Script Expressions
205+
206+
Script expressions (i.e, `(...)` and `?(...)`) are statically evaluated 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 `?(os.exit())` will not get evaluated since `os` would yield a `ReferenceError`.
207+
208+
#### Grammar
209+
210+
This project uses a formal PEG [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. `[` => `$`).
211+
212+
#### Other Minor Differences
213+
214+
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:
215+
216+
- strings in subscripts may now be double-quoted
217+
- final `step` arguments in slice operators may now be negative
218+
- script expressions may now contain `.` and `@` characters not referring to instance variables
219+
- subscripts no longer act as character slices on string elements
220+
- non-ascii non-word characters are no-longer valid in member identifier names; use quoted subscript strings instead (e.g., `$['$']` instead of `$.$`)
221+
- unions now yield real unions with no duplicates rather than concatenated results
222+
223+
224+
## Differences from David Chester's Javascript Implementation
225+
226+
#### Grammar
227+
228+
This implementation aims to be fully compatible with David Chester's Javascript implementation. All applicable test cases were ported from David Chester's project to Lua, and they all pass.
229+
230+
#### API Methods
231+
232+
Some of David Chester's API methods are not implemented in Lua JsonPath:
233+
234+
- `jp.parent(obj, pathExpression)`
235+
- `jp.apply(obj, pathExpression, fn)`
236+
- `jp.stringify(path)`
237+
238+
The `jp.value` API method does not support the third argument (`newValue`).
239+
240+
The `jp.grammer` API method was added in Lua JsonPath. The `jp.query`, `jp.value`, `jp.paths`, `jp.nodes` functions accept abstract syntax trees returned by `lpeg.match` for Lua JsonPath expressions matched using `jp.grammer`. This is for embedding Lua JsonPath into higher level grammers.
241+
242+
243+
## License
244+
245+
*The MIT License*
246+
247+
Copyright (c) 2016 Frank Edelhaeuser
248+
249+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
250+
251+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
252+
253+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

jsonpath-1.0-1.rockspec

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package = 'jsonpath'
2+
version = '1.0-1'
3+
source = {
4+
url = 'git://github.com/mrpace2/lua-jsonpath',
5+
tag = '1.0'
6+
}
7+
description = {
8+
summary = 'Query Lua data structures with JsonPath expressions. Robust and safe JsonPath engine for Lua.',
9+
detailed = [[
10+
This library implements Stefan Goessner's JsonPath syntax (http://goessner.net/articles/JsonPath/) in Lua.
11+
Lua JsonPath is compatible with David Chester's Javascript implementation (https://github.com/dchester/jsonpath).
12+
13+
The Lua JsonPath library was written from scratch by Frank Edelhaeuser. It's a pure Lua implementation
14+
based on a PEG grammer handled by Roberto Ierusalimschy's fabulous LPeg pattern-matching library
15+
(http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html).
16+
]],
17+
homepage = 'https://github.com/mrpace2/lua-jsonpath',
18+
license = 'MIT'
19+
}
20+
dependencies = {
21+
'lua >= 5.1',
22+
'lpeg >= 1.0.0'
23+
}
24+
build = {
25+
type = 'builtin',
26+
modules = {
27+
jsonpath = 'jsonpath.lua'
28+
},
29+
copy_directories = {
30+
'test'
31+
}
32+
}

0 commit comments

Comments
 (0)