janeway-jsonpath 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +201 -28
- data/bin/janeway +36 -9
- data/lib/janeway/ast/expression.rb +1 -2
- data/lib/janeway/enumerator.rb +43 -0
- data/lib/janeway/error.rb +6 -9
- data/lib/janeway/functions/length.rb +1 -1
- data/lib/janeway/interpreter.rb +124 -19
- data/lib/janeway/interpreters/array_slice_selector_deleter.rb +41 -0
- data/lib/janeway/interpreters/array_slice_selector_interpreter.rb +15 -10
- data/lib/janeway/interpreters/base.rb +26 -2
- data/lib/janeway/interpreters/binary_operator_interpreter.rb +10 -7
- data/lib/janeway/interpreters/child_segment_deleter.rb +19 -0
- data/lib/janeway/interpreters/child_segment_interpreter.rb +48 -8
- data/lib/janeway/interpreters/current_node_interpreter.rb +5 -3
- data/lib/janeway/interpreters/descendant_segment_interpreter.rb +13 -9
- data/lib/janeway/interpreters/filter_selector_deleter.rb +65 -0
- data/lib/janeway/interpreters/filter_selector_interpreter.rb +54 -14
- data/lib/janeway/interpreters/function_interpreter.rb +7 -5
- data/lib/janeway/interpreters/index_selector_deleter.rb +26 -0
- data/lib/janeway/interpreters/index_selector_interpreter.rb +3 -2
- data/lib/janeway/interpreters/name_selector_deleter.rb +27 -0
- data/lib/janeway/interpreters/name_selector_interpreter.rb +19 -4
- data/lib/janeway/interpreters/root_node_deleter.rb +34 -0
- data/lib/janeway/interpreters/root_node_interpreter.rb +4 -2
- data/lib/janeway/interpreters/tree_constructor.rb +20 -1
- data/lib/janeway/interpreters/unary_operator_interpreter.rb +2 -2
- data/lib/janeway/interpreters/wildcard_selector_deleter.rb +32 -0
- data/lib/janeway/interpreters/wildcard_selector_interpreter.rb +26 -11
- data/lib/janeway/interpreters/yielder.rb +50 -12
- data/lib/janeway/lexer.rb +1 -1
- data/lib/janeway/normalized_path.rb +66 -0
- data/lib/janeway/parser.rb +3 -3
- data/lib/janeway/query.rb +70 -0
- data/lib/janeway/version.rb +1 -1
- data/lib/janeway.rb +16 -28
- metadata +12 -3
- data/lib/janeway/ast/query.rb +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee00e4f5c1c77042409d76e6de5a287dfe0d5bcdaa2c150db2d11fa7e4037228
|
4
|
+
data.tar.gz: 7b95f568aa555bb5b9959a2ced3677d7ab7b0e877c69e34db29abbdff5211e50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57a64e5946f8375ef13bc634dd9e860588fd7ff7d2c6b2f3b6c14de640ba1ba26e4a4fcc12ba71958853728626d846ed067392ca876efd94f02f0a41c2d2ac5b
|
7
|
+
data.tar.gz: 238e8cdb81ddab8674c22eb2150c5b7522e7012eac965f264486017c7e51328a0eea23d3d352698c72eeb0275a1924a1b89575104eaf64d3796a52cdb76499dd
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Janeway JSONPath parser
|
2
2
|
|
3
|
-
This is a [
|
3
|
+
This is a [JSONPath](https://goessner.net/articles/JsonPath/) parser.
|
4
4
|
It strictly follows [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html) and passes the [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite).
|
5
5
|
|
6
6
|
It reads a JSON input file and a query, and uses the query to find and return a set of matching values from the input.
|
@@ -8,8 +8,8 @@ This does for JSON the same job that XPath does for XML.
|
|
8
8
|
|
9
9
|
This project includes:
|
10
10
|
|
11
|
-
* command-line tool to run jsonpath queries on a JSON input
|
12
11
|
* ruby library to run jsonpath queries on a JSON input
|
12
|
+
* command-line tool to do the same
|
13
13
|
|
14
14
|
**Contents**
|
15
15
|
|
@@ -23,13 +23,13 @@ This project includes:
|
|
23
23
|
|
24
24
|
Install the gem from the command-line:
|
25
25
|
```
|
26
|
-
gem install janeway-jsonpath
|
26
|
+
gem install janeway-jsonpath
|
27
27
|
```
|
28
28
|
|
29
29
|
or add it to your Gemfile:
|
30
30
|
|
31
31
|
```
|
32
|
-
gem 'janeway-jsonpath', '~> 0.
|
32
|
+
gem 'janeway-jsonpath', '~> 0.4.0'
|
33
33
|
```
|
34
34
|
|
35
35
|
### Usage
|
@@ -41,45 +41,92 @@ Use single quotes around the JSON query to avoid shell interaction.
|
|
41
41
|
Example:
|
42
42
|
|
43
43
|
```
|
44
|
-
$
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
44
|
+
$ cat store.json
|
45
|
+
{ "store": {
|
46
|
+
"book": [
|
47
|
+
{ "category": "reference",
|
48
|
+
"author": "Nigel Rees",
|
49
|
+
"title": "Sayings of the Century",
|
50
|
+
"price": 8.95
|
51
|
+
},
|
52
|
+
{ "category": "fiction",
|
53
|
+
"author": "Evelyn Waugh",
|
54
|
+
"title": "Sword of Honour",
|
55
|
+
"price": 12.99
|
56
|
+
},
|
57
|
+
{ "category": "fiction",
|
58
|
+
"author": "Herman Melville",
|
59
|
+
"title": "Moby Dick",
|
60
|
+
"isbn": "0-553-21311-3",
|
61
|
+
"price": 8.99
|
62
|
+
},
|
63
|
+
{ "category": "fiction",
|
64
|
+
"author": "J. R. R. Tolkien",
|
65
|
+
"title": "The Lord of the Rings",
|
66
|
+
"isbn": "0-395-19395-8",
|
67
|
+
"price": 22.99,
|
68
|
+
"value": true
|
69
|
+
}
|
70
|
+
],
|
71
|
+
"bicycle": {
|
72
|
+
"color": "red",
|
73
|
+
"price": 399
|
74
|
+
}
|
58
75
|
}
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
### Find and return matching values
|
80
|
+
$ janeway '$..book[?(@["price"] == 8.95 || @["price"] == 8.99)].title' store.json
|
81
|
+
[
|
82
|
+
"Sayings of the Century",
|
83
|
+
"Moby Dick"
|
59
84
|
]
|
85
|
+
|
86
|
+
### Delete matching values from the input, print what remains
|
87
|
+
$ janeway -d '$.store.book' store.json
|
88
|
+
{
|
89
|
+
"store": {
|
90
|
+
"bicycle": {
|
91
|
+
"color": "red",
|
92
|
+
"price": 399
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
60
96
|
```
|
61
97
|
|
62
98
|
You can also pipe JSON into it:
|
63
99
|
```
|
64
|
-
$ cat
|
100
|
+
$ cat store.json | janeway '$..book[?(@.price<10)]'
|
65
101
|
```
|
66
102
|
|
67
103
|
See the help message for more capabilities: `janeway --help`
|
68
104
|
|
69
105
|
#### Janeway ruby libarary
|
70
106
|
|
71
|
-
Here's an example of using Janeway to
|
107
|
+
Here's an example of ruby code using Janeway to find values from a JSON document:
|
108
|
+
To use the Janeway library in ruby code, providing a jsonpath query and an input object (Hash or Array) to search.
|
72
109
|
```ruby
|
73
|
-
require 'janeway'
|
74
|
-
require 'json'
|
110
|
+
require 'janeway'
|
111
|
+
require 'json'
|
75
112
|
|
76
|
-
data = JSON.parse(File.read(ARGV.first))
|
77
|
-
|
113
|
+
data = JSON.parse(File.read(ARGV.first))
|
114
|
+
Janeway.enum_for('$.store.book[0].title', data)
|
78
115
|
```
|
116
|
+
This returns an Enumerator, which offers instance methods for using the query to operate on matching values in the input object.
|
117
|
+
|
118
|
+
Following are examples showing how to work with the Enumerator methods.
|
79
119
|
|
80
|
-
|
120
|
+
##### #search
|
81
121
|
|
82
|
-
|
122
|
+
Returns all values that match the query.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
results = Janeway.enum_for('$..book[?(@.price<10)]', data).search
|
126
|
+
# Returns every book in the store cheaper than $10
|
127
|
+
```
|
128
|
+
|
129
|
+
Alternatively, compile the query once, and share it between threads or ractors with different data sources:
|
83
130
|
|
84
131
|
```ruby
|
85
132
|
# Create ractors with their own data sources
|
@@ -88,7 +135,7 @@ The Janeway::AST::Query object is not modified after parsing, so it is easy to f
|
|
88
135
|
Ractor.new(index) do |i|
|
89
136
|
query = receive
|
90
137
|
data = JSON.parse File.read("input-file-#{i}.json")
|
91
|
-
puts query.
|
138
|
+
puts query.enum_for(data).search
|
92
139
|
end
|
93
140
|
end
|
94
141
|
|
@@ -97,6 +144,132 @@ The Janeway::AST::Query object is not modified after parsing, so it is easy to f
|
|
97
144
|
ractors.each { |ractor| ractor.send(query).take }
|
98
145
|
```
|
99
146
|
|
147
|
+
##### #each
|
148
|
+
|
149
|
+
Iterates through matches one by one, without holding the entire set in memory.
|
150
|
+
Janeway's #each iteration is particularly powerful, as it provides context for each match.
|
151
|
+
The matched value is yielded:
|
152
|
+
```ruby
|
153
|
+
data = {
|
154
|
+
'birds' => ['eagle', 'stork', 'cormorant'] }
|
155
|
+
'dogs' => ['poodle', 'pug', 'saint bernard'] },
|
156
|
+
}
|
157
|
+
Janeway.enum_for('$.birds.*', data).each do |bird|
|
158
|
+
puts "the bird is a #{bird}"
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
This allows the matched value to be modified in place:
|
163
|
+
```ruby
|
164
|
+
Janeway.enum_for('$.birds[? @=="storck"]', data).each do |bird|
|
165
|
+
bird.gsub!('ck', 'k') # Fix a typo: "storck" => "stork"
|
166
|
+
end
|
167
|
+
# input list is now ['eagle', 'stork', 'cormorant']
|
168
|
+
```
|
169
|
+
|
170
|
+
However, this is not enough to replace the matched value:
|
171
|
+
```ruby
|
172
|
+
Janeway.enum_for('$.birds', data).each do |bird|
|
173
|
+
bird = "bald eagle" if bird == 'eagle'
|
174
|
+
# local variable 'bird' now points to a new value, but the original list is unchanged
|
175
|
+
end
|
176
|
+
# input list is still ['eagle', 'stork', 'cormorant']
|
177
|
+
```
|
178
|
+
|
179
|
+
The second and third yield parameters are the object that contains the value, and the array index or hash key of the value.
|
180
|
+
This allows the list or hash to be modified:
|
181
|
+
```ruby
|
182
|
+
Janeway.enum_for('$.birds[? @=="eagle"]', data).each do |_bird, parent, index|
|
183
|
+
parent[index] = "golden eagle"
|
184
|
+
end
|
185
|
+
# input list is now ['golden eagle', 'stork', 'cormorant']
|
186
|
+
```
|
187
|
+
|
188
|
+
Lastly, the #each iterator's fourth yield parameter is the [normalized path](https://www.rfc-editor.org/rfc/rfc9535.html#name-normalized-paths) to the matched value.
|
189
|
+
This is a jsonpath query string that uniquely points to the matched value.
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
# Collect the normalized path of every object in the input, at all levels
|
193
|
+
paths = []
|
194
|
+
Janeway.enum_for('$..*', data).each do |_bird, _parent, _index, path| do
|
195
|
+
paths << path
|
196
|
+
end
|
197
|
+
paths
|
198
|
+
# ["$['birds']", "$['dogs']", "$['birds'][0]", "$['birds'][1]", "$['birds'][2]", "$['dogs'][0]", "$['dogs'][1]", "$['dogs'][2]"]
|
199
|
+
```
|
200
|
+
|
201
|
+
##### #delete
|
202
|
+
|
203
|
+
The '#delete' method deletes matched values from the input.
|
204
|
+
```ruby
|
205
|
+
# delete any bird whose name is "eagle" or starts with "s"
|
206
|
+
Janeway.enum_for('$.birds[? @=="eagle" || search(@, "^s")]', data).delete
|
207
|
+
# input bird list is now ['cormorant']
|
208
|
+
|
209
|
+
# delete all dog names
|
210
|
+
Janeway.enum_for('$.dogs.*', data).delete
|
211
|
+
# dog list is now []
|
212
|
+
```
|
213
|
+
|
214
|
+
|
215
|
+
The `Janeway.enum_for` and `Janeway::Query#on` methods return an enumerator, so you can use the usual ruby enumerator methods, such as:
|
216
|
+
|
217
|
+
##### #map
|
218
|
+
Return the matched elements, as modified by the block.
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
# take a dollar off every price in the store
|
222
|
+
sale_prices = Janeway.enum_for('$.store..price', data).map { |price| price - 1 }
|
223
|
+
# [7.95, 11.99, 7.99, 21.99, 398]
|
224
|
+
```
|
225
|
+
|
226
|
+
##### #select (alias #find_all)
|
227
|
+
|
228
|
+
Return only values that match the JSONPath query and also return a truthy value from the block.
|
229
|
+
This solves a common JSON problem: You want to do a numeric comparison on a value in your JSON, but the JSON value is stored as a string type.
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
# Poorly serialized, the prices are strings
|
233
|
+
data =
|
234
|
+
{ "store" => {
|
235
|
+
"book" => [
|
236
|
+
{ "title" => "Sayings of the Century", "price" => "8.95" },
|
237
|
+
{ "title" => "Sword of Honour", "price" => "12.99" },
|
238
|
+
{ "title" => "Moby Dick", "price" => "8.99" },
|
239
|
+
{ "title" => "The Lord of the Rings", "price" => "22.99" },
|
240
|
+
]
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
# Can't use a filter query with a numeric comparison on a string price
|
245
|
+
Janeway.enum_for('$.store.book[? @.price > 10.00]', data).find_all
|
246
|
+
# result: []
|
247
|
+
|
248
|
+
# Solve the problem with ruby by filtering with #select and converting string to number:
|
249
|
+
Janeway.enum_for('$.store.book.*', data).select { |book| book['price'].to_f > 10 }
|
250
|
+
# result: [{"title" => "Sword of Honour", "price" => "12.99"}, {"title" => "The Lord of the Rings", "price" => "22.99"}]
|
251
|
+
```
|
252
|
+
|
253
|
+
##### #reject
|
254
|
+
|
255
|
+
Return only values that match the JSONPath query and also return false from the block.
|
256
|
+
|
257
|
+
##### #filter_map
|
258
|
+
|
259
|
+
Combines #select and #map.
|
260
|
+
Return values that match the jsonpath query and return truthy values from the block.
|
261
|
+
Instead of returning the value from the data, return the result of the block.
|
262
|
+
|
263
|
+
##### #find
|
264
|
+
|
265
|
+
Return the first value that matches the jsonpath query and also returns a truthy value from the block
|
266
|
+
```ruby
|
267
|
+
Janeway.enum_for('$.store.book[? @.price >= 10.99]', data).find { |book| book['title'].start_with?('T') }
|
268
|
+
# [ "The Lord of the Rings" ]
|
269
|
+
```
|
270
|
+
|
271
|
+
There are many other Enumerable methods too, see the ruby Enumerable module documenation for more.
|
272
|
+
|
100
273
|
### Related Projects
|
101
274
|
|
102
275
|
- [joshbuddy/jsonpath](https://github.com/joshbuddy/jsonpath)
|
@@ -121,7 +294,7 @@ Also there are many non-ruby implementations of RFC 9535, here are just a few:
|
|
121
294
|
* don't use `eval`, which is known to be an attack vector
|
122
295
|
* be simple and fast with minimal dependencies
|
123
296
|
* provide ruby-like accessors (eg. #each, #delete_if) for processing results
|
124
|
-
*
|
297
|
+
* idiomatic, linted ruby 3 code with frozen string literals everywhere
|
125
298
|
|
126
299
|
### Non-goals
|
127
300
|
|
data/bin/janeway
CHANGED
@@ -15,14 +15,15 @@ HELP = <<~HELP_TEXT.freeze
|
|
15
15
|
#{SCRIPT_NAME} [QUERY] [FILENAME]
|
16
16
|
|
17
17
|
Purpose:
|
18
|
-
Print the result of applying a
|
18
|
+
Print the result of applying a JSONPath query to a JSON input.
|
19
19
|
|
20
|
-
QUERY is a
|
20
|
+
QUERY is a JSONPath query. Quote it with single quotes to avoid shell errors.
|
21
21
|
|
22
22
|
FILENAME is the path to a JSON file to use as input.
|
23
23
|
Alternately, input JSON can be provided on STDIN.
|
24
|
+
If input is not provided then the query is just checked for correctness.
|
24
25
|
|
25
|
-
For an introduction to
|
26
|
+
For an introduction to JSONPath, see https://goessner.net/articles/JsonPath/
|
26
27
|
For the complete reference, see https://www.rfc-editor.org/info/rfc9535
|
27
28
|
|
28
29
|
Examples:
|
@@ -32,7 +33,7 @@ HELP = <<~HELP_TEXT.freeze
|
|
32
33
|
HELP_TEXT
|
33
34
|
|
34
35
|
# Command-line options
|
35
|
-
Options = Struct.new(:query, :query_file, :input, :compact_output, :verbose)
|
36
|
+
Options = Struct.new(:query, :query_file, :input, :compact_output, :delete, :verbose)
|
36
37
|
|
37
38
|
# Parse the command-line arguments.
|
38
39
|
# This includes both bare words and hyphenated options.
|
@@ -60,6 +61,7 @@ def parse_options(argv)
|
|
60
61
|
|
61
62
|
opts.on('-q', '--query FILE', 'Read jsonpath query from file') { |o| options.query_file = o }
|
62
63
|
opts.on('-c', '--compact', 'Express result in compact json format') { options.compact_output = true }
|
64
|
+
opts.on('-d', '--delete', 'Print the input, with matching values deleted') { options.delete = true }
|
63
65
|
opts.on('--version', 'Show version number') { abort(Janeway::VERSION) }
|
64
66
|
opts.on('-h', '--help', 'Show this help message') { abort(opts.to_s) }
|
65
67
|
end
|
@@ -74,8 +76,9 @@ end
|
|
74
76
|
def read_query(path, argv)
|
75
77
|
return File.read(path).strip if path
|
76
78
|
|
77
|
-
query
|
78
|
-
|
79
|
+
# Assume query is the first arg that is not a filename
|
80
|
+
query = argv.reject { File.exist?(_1) }.first
|
81
|
+
abort('No JSONPath query received, provide one on the command line.') unless query
|
79
82
|
|
80
83
|
argv.delete(query)
|
81
84
|
query
|
@@ -91,7 +94,7 @@ def read_input(path)
|
|
91
94
|
elsif !$stdin.tty?
|
92
95
|
$stdin.read
|
93
96
|
else
|
94
|
-
|
97
|
+
return # no input json provided
|
95
98
|
end
|
96
99
|
parse_json(json)
|
97
100
|
end
|
@@ -108,9 +111,31 @@ rescue JSON::JSONError => e
|
|
108
111
|
abort "Input is not valid JSON: #{msg}"
|
109
112
|
end
|
110
113
|
|
114
|
+
# Just pares the query ane then exit.
|
115
|
+
# Useful for testing whether a query is valid.
|
116
|
+
# @param query [String] jsonpath
|
117
|
+
def parse_query_and_exit(query)
|
118
|
+
Janeway.compile(query)
|
119
|
+
puts 'Query is valid. Provide input json to run the query.'
|
120
|
+
exit(0)
|
121
|
+
end
|
122
|
+
|
111
123
|
# @param options [Options]
|
112
124
|
def main(options)
|
113
|
-
|
125
|
+
parse_query_and_exit(options.query) unless options.input
|
126
|
+
|
127
|
+
enum = Janeway.enum_for(options.query, options.input)
|
128
|
+
if options.delete
|
129
|
+
results = enum.delete
|
130
|
+
if options.compact_output
|
131
|
+
puts JSON.generate(options.input)
|
132
|
+
else
|
133
|
+
puts JSON.pretty_generate(options.input)
|
134
|
+
end
|
135
|
+
exit(0)
|
136
|
+
else
|
137
|
+
results = enum.search
|
138
|
+
end
|
114
139
|
|
115
140
|
if options.compact_output
|
116
141
|
puts JSON.generate(results)
|
@@ -123,7 +148,9 @@ begin
|
|
123
148
|
options = parse_args(ARGV.dup)
|
124
149
|
main(options)
|
125
150
|
rescue Janeway::Error => e
|
126
|
-
|
151
|
+
warn "Error: #{e.message}\nQuery: #{e.query}\n"
|
152
|
+
warn " #{' ' * e.location.col}^" if e.location
|
153
|
+
exit(1)
|
127
154
|
rescue Interrupt, Errno::EPIPE
|
128
155
|
abort("\n")
|
129
156
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
require_relative 'helpers'
|
4
3
|
require_relative 'error'
|
5
4
|
|
@@ -23,7 +22,7 @@ module Janeway
|
|
23
22
|
def initialize(val = nil)
|
24
23
|
# don't set the instance variable if unused, because it makes the
|
25
24
|
# "#inspect" output cleaner in rspec test failures
|
26
|
-
@value = val unless val.nil? # false must be stored though!
|
25
|
+
@value = val unless val.nil? # literal false must be stored though!
|
27
26
|
end
|
28
27
|
|
29
28
|
# @return [String]
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Janeway
|
4
|
+
# Enumerator combines a parsed JSONpath query with input.
|
5
|
+
# It provides enumerator methods.
|
6
|
+
class Enumerator
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# @param query [Janeway::Query] @param input [Array, Hash]
|
10
|
+
def initialize(query, input)
|
11
|
+
@query = query
|
12
|
+
@input = input
|
13
|
+
|
14
|
+
raise ArgumentError, "expect Janeway::Query, got #{query.inspect}" unless query.is_a?(Query)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return a list of values from the input data that match the jsonpath query
|
18
|
+
#
|
19
|
+
# @return [Array] all matched objects
|
20
|
+
def search
|
21
|
+
Janeway::Interpreter.new(@query).interpret(@input)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Iterate through each value matched by the JSONPath query.
|
25
|
+
#
|
26
|
+
# @yieldparam [Object] value matched by query
|
27
|
+
# @yieldparam [Array, Hash] parent object that contains the value
|
28
|
+
# @yieldparam [String, Integer] hash key or array index of the value within the parent object
|
29
|
+
# @yieldparam [String] normalized jsonpath that uniqely points to this value
|
30
|
+
# @return [void]
|
31
|
+
def each(&block)
|
32
|
+
return enum_for(:each) unless block_given?
|
33
|
+
|
34
|
+
Janeway::Interpreter.new(@query, as: :iterator, &block).interpret(@input)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Delete each value matched by the JSONPath query.
|
38
|
+
# @return [Array, Hash]
|
39
|
+
def delete
|
40
|
+
Janeway::Interpreter.new(@query, as: :deleter).interpret(@input)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/janeway/error.rb
CHANGED
@@ -3,7 +3,10 @@
|
|
3
3
|
require_relative 'location'
|
4
4
|
|
5
5
|
module Janeway
|
6
|
-
#
|
6
|
+
# Error class for Janeway.
|
7
|
+
# Contains a copy of the jsonpath query string.
|
8
|
+
# Lexer errors may also include the index into the query string that
|
9
|
+
# points at which token was being lexed at the time of the error.
|
7
10
|
class Error < StandardError
|
8
11
|
# @return [String]
|
9
12
|
attr_reader :query
|
@@ -14,16 +17,10 @@ module Janeway
|
|
14
17
|
# @param message [String] error message
|
15
18
|
# @param query [String] entire query string
|
16
19
|
# @param location [Location] location of error
|
17
|
-
def initialize(
|
18
|
-
super("Jsonpath query #{query} - #{
|
20
|
+
def initialize(msg, query = nil, location = nil)
|
21
|
+
super("Jsonpath query #{query} - #{msg}")
|
19
22
|
@query = query
|
20
23
|
@location = location
|
21
24
|
end
|
22
|
-
|
23
|
-
def detailed_message
|
24
|
-
msg = "Error: #{message}\nQuery: #{query}\n"
|
25
|
-
msg += "#{' ' * location.col}^" if @location
|
26
|
-
msg
|
27
|
-
end
|
28
25
|
end
|
29
26
|
end
|
@@ -19,7 +19,7 @@ module Janeway
|
|
19
19
|
unless arg.singular_query? || arg.literal?
|
20
20
|
raise Error, "Invalid parameter - length() expects literal value or singular query, got #{arg.value.inspect}"
|
21
21
|
end
|
22
|
-
raise
|
22
|
+
raise err('Too many parameters for length() function call') unless current.type == :group_end
|
23
23
|
|
24
24
|
# Meaning of return value depends on the JSON type:
|
25
25
|
# * string - number of Unicode scalar values in the string.
|