jsonpath 0.9.3 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '048116fe74d7b281fbcf58d8f5d1aa44c359267bf179e58376527032e58c0094'
4
- data.tar.gz: 4d91c8953e2750322d486f372b165507c4f383a42c11d07987cc49630abea5b6
3
+ metadata.gz: ffcdd9076a2bd58ead93c850c8a1ed842a202e6db62314fb2b6c8fd5f8650fb5
4
+ data.tar.gz: 3d6624bd18086aa892f93bff141dd7bf33e516e02c3fd7e8828db13eb3a84436
5
5
  SHA512:
6
- metadata.gz: fb899c515175b16b0f9c1001ae7525f14d086b4bf844779cef819c9e634511445c0ee1aa033c7e51c9419aa1de1e22ae8e4670e7fbaa162b27d5c819ebef0846
7
- data.tar.gz: d303bb0188617e52133250571c43c0262b02cd1f586edb2d05683f30e6e26e17dfaeb81a3a62e40c4b838374e9b9354fbf012d03591fa7fb465549ff9148ddd8
6
+ metadata.gz: 492ed8f729cfdb038c2b0303f49bf2bf1bfb85bd4a7d6ae2e37c2b559bf288b73110db65b2c1642f4e1954cde213d10488ab996ad9261c108f253f03aeb41d12
7
+ data.tar.gz: 2642b36da6a171649a9b0dc2e973994a7d3268e71a62ea290e75f91bd7f7af621b0b0dd6d9fb6a1f7148943f2c5ce6965f8de0db69db4041bc973576b7694425
@@ -0,0 +1,33 @@
1
+ name: test
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ test:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby-version:
11
+ - '2.6'
12
+ - '2.7'
13
+ - '3.2'
14
+ - '3.1'
15
+ - '3.0'
16
+ - ruby-head
17
+ - jruby-head
18
+ - truffleruby-head
19
+ runs-on:
20
+ - ubuntu-latest
21
+
22
+ runs-on: ${{ matrix.runs-on }}
23
+
24
+ steps:
25
+
26
+ - uses: actions/checkout@v2
27
+
28
+ - uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{ matrix.ruby-version }}
31
+ bundler-cache: true
32
+
33
+ - run: bundle exec rake test
data/.gitignore CHANGED
@@ -3,3 +3,8 @@ Gemfile.lock
3
3
  coverage/*
4
4
  doc/*
5
5
  .yardoc
6
+ .DS_Store
7
+ .idea
8
+ vendor
9
+ .tags
10
+ *.gem
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2018-07-07 21:29:45 +0200 using RuboCop version 0.57.2.
3
+ # on 2019-01-25 09:23:04 +0100 using RuboCop version 0.63.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -18,31 +18,32 @@ Lint/IneffectiveAccessModifier:
18
18
  Exclude:
19
19
  - 'lib/jsonpath.rb'
20
20
 
21
- # Offense count: 16
21
+ # Offense count: 17
22
22
  Metrics/AbcSize:
23
- Max: 54
23
+ Max: 60
24
24
 
25
25
  # Offense count: 2
26
26
  # Configuration parameters: CountComments, ExcludedMethods.
27
+ # ExcludedMethods: refine
27
28
  Metrics/BlockLength:
28
- Max: 33
29
+ Max: 37
29
30
 
30
31
  # Offense count: 1
31
32
  # Configuration parameters: CountBlocks.
32
33
  Metrics/BlockNesting:
33
34
  Max: 4
34
35
 
35
- # Offense count: 2
36
+ # Offense count: 3
36
37
  # Configuration parameters: CountComments.
37
38
  Metrics/ClassLength:
38
- Max: 578
39
+ Max: 739
39
40
 
40
- # Offense count: 6
41
+ # Offense count: 7
41
42
  Metrics/CyclomaticComplexity:
42
- Max: 18
43
+ Max: 20
43
44
 
44
- # Offense count: 24
45
- # Configuration parameters: CountComments.
45
+ # Offense count: 26
46
+ # Configuration parameters: CountComments, ExcludedMethods.
46
47
  Metrics/MethodLength:
47
48
  Max: 52
48
49
 
@@ -53,9 +54,16 @@ Metrics/ParameterLists:
53
54
 
54
55
  # Offense count: 6
55
56
  Metrics/PerceivedComplexity:
56
- Max: 20
57
+ Max: 21
57
58
 
58
59
  # Offense count: 1
60
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
61
+ # AllowedNames: io, id, to, by, on, in, at, ip, db
62
+ Naming/UncommunicativeMethodParamName:
63
+ Exclude:
64
+ - 'lib/jsonpath/parser.rb'
65
+
66
+ # Offense count: 15
59
67
  # Configuration parameters: AllowedChars.
60
68
  Style/AsciiComments:
61
69
  Exclude:
@@ -69,11 +77,12 @@ Style/Documentation:
69
77
  - 'lib/jsonpath/enumerable.rb'
70
78
  - 'lib/jsonpath/proxy.rb'
71
79
 
72
- # Offense count: 1
80
+ # Offense count: 3
73
81
  # Configuration parameters: MinBodyLength.
74
82
  Style/GuardClause:
75
83
  Exclude:
76
84
  - 'lib/jsonpath/enumerable.rb'
85
+ - 'lib/jsonpath/parser.rb'
77
86
 
78
87
  # Offense count: 2
79
88
  # Cop supports --auto-correct.
@@ -81,16 +90,22 @@ Style/IfUnlessModifier:
81
90
  Exclude:
82
91
  - 'lib/jsonpath/enumerable.rb'
83
92
 
84
- # Offense count: 2
93
+ # Offense count: 1
94
+ Style/MultipleComparison:
95
+ Exclude:
96
+ - 'lib/jsonpath/parser.rb'
97
+
98
+ # Offense count: 3
85
99
  # Cop supports --auto-correct.
86
- # Configuration parameters: AutoCorrect, EnforcedStyle.
100
+ # Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
87
101
  # SupportedStyles: predicate, comparison
88
102
  Style/NumericPredicate:
89
103
  Exclude:
90
104
  - 'spec/**/*'
91
105
  - 'lib/jsonpath/enumerable.rb'
106
+ - 'lib/jsonpath/parser.rb'
92
107
 
93
- # Offense count: 2
108
+ # Offense count: 3
94
109
  # Cop supports --auto-correct.
95
110
  # Configuration parameters: EnforcedStyle, AllowInnerSlashes.
96
111
  # SupportedStyles: slashes, percent_r, mixed
@@ -98,15 +113,15 @@ Style/RegexpLiteral:
98
113
  Exclude:
99
114
  - 'lib/jsonpath/parser.rb'
100
115
 
101
- # Offense count: 3
116
+ # Offense count: 4
102
117
  # Cop supports --auto-correct.
103
118
  Style/RescueModifier:
104
119
  Exclude:
105
120
  - 'lib/jsonpath/enumerable.rb'
106
121
  - 'lib/jsonpath/parser.rb'
107
122
 
108
- # Offense count: 71
123
+ # Offense count: 89
109
124
  # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
110
125
  # URISchemes: http, https
111
126
  Metrics/LineLength:
112
- Max: 175
127
+ Max: 296
data/Gemfile CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  source 'http://rubygems.org'
4
4
  gemspec
5
- gem 'rubocop', require: true, group: :test
5
+ # gem 'rubocop', require: true, group: :test
6
6
  gem 'simplecov', require: false, group: :test
data/README.md CHANGED
@@ -4,8 +4,8 @@ This is an implementation of http://goessner.net/articles/JsonPath/.
4
4
 
5
5
  ## What is JsonPath?
6
6
 
7
- JsonPath is a way of addressing elements within a JSON object. Similar to xpath of yore, JsonPath lets you
8
- traverse a json object and manipulate or access it.
7
+ JsonPath is a way of addressing elements within a JSON object. Similar to xpath
8
+ of yore, JsonPath lets you traverse a json object and manipulate or access it.
9
9
 
10
10
  ## Usage
11
11
 
@@ -15,8 +15,8 @@ There is stand-alone usage through the binary `jsonpath`
15
15
 
16
16
  jsonpath [expression] (file|string)
17
17
 
18
- If you omit the second argument, it will read stdin, assuming one valid JSON object
19
- per line. Expression must be a valid jsonpath expression.
18
+ If you omit the second argument, it will read stdin, assuming one valid JSON
19
+ object per line. Expression must be a valid jsonpath expression.
20
20
 
21
21
  ### Library
22
22
 
@@ -40,8 +40,8 @@ json = <<-HERE_DOC
40
40
  HERE_DOC
41
41
  ```
42
42
 
43
- Now that we have a JSON object, let's get all the prices present in the object. We create an object for the path
44
- in the following way.
43
+ Now that we have a JSON object, let's get all the prices present in the object.
44
+ We create an object for the path in the following way.
45
45
 
46
46
  ```ruby
47
47
  path = JsonPath.new('$..price')
@@ -54,14 +54,15 @@ path.on(json)
54
54
  # => [19.95, 8.95, 12.99, 8.99, 22.99]
55
55
  ```
56
56
 
57
- Or on some other object ...
57
+ Or reuse it later on some other object (thread safe) ...
58
58
 
59
59
  ```ruby
60
60
  path.on('{"books":[{"title":"A Tale of Two Somethings","price":18.88}]}')
61
61
  # => [18.88]
62
62
  ```
63
63
 
64
- You can also just combine this into one mega-call with the convenient `JsonPath.on` method.
64
+ You can also just combine this into one mega-call with the convenient
65
+ `JsonPath.on` method.
65
66
 
66
67
  ```ruby
67
68
  JsonPath.on(json, '$..author')
@@ -73,29 +74,36 @@ Of course the full JsonPath syntax is supported, such as array slices
73
74
  ```ruby
74
75
  JsonPath.new('$..book[::2]').on(json)
75
76
  # => [
76
- # {"price"=>8.95, "category"=>"reference", "author"=>"Nigel Rees", "title"=>"Sayings of the Century"},
77
- # {"price"=>8.99, "category"=>"fiction", "author"=>"Herman Melville", "title"=>"Moby Dick", "isbn"=>"0-553-21311-3"}
77
+ # {"price" => 8.95, "category" => "reference", "title" => "Sayings of the Century", "author" => "Nigel Rees"},
78
+ # {"price" => 8.99, "category" => "fiction", "isbn" => "0-553-21311-3", "title" => "Moby Dick", "author" => "Herman Melville","color" => "blue"},
78
79
  # ]
79
80
  ```
80
81
 
81
- ...and evals.
82
+ ...and evals, including those with conditional operators
82
83
 
83
84
  ```ruby
84
- JsonPath.new('$..price[?(@ < 10)]').on(json)
85
+ JsonPath.new("$..price[?(@ < 10)]").on(json)
85
86
  # => [8.95, 8.99]
87
+
88
+ JsonPath.new("$..book[?(@['price'] == 8.95 || @['price'] == 8.99)].title").on(json)
89
+ # => ["Sayings of the Century", "Moby Dick"]
90
+
91
+ JsonPath.new("$..book[?(@['price'] == 8.95 && @['price'] == 8.99)].title").on(json)
92
+ # => []
86
93
  ```
87
94
 
88
- There is a convenience method, `#first` that gives you the first element for a JSON object and path.
95
+ There is a convenience method, `#first` that gives you the first element for a
96
+ JSON object and path.
89
97
 
90
98
  ```ruby
91
- JsonPath.new('$..color').first(object)
99
+ JsonPath.new('$..color').first(json)
92
100
  # => "red"
93
101
  ```
94
102
 
95
103
  As well, we can directly create an `Enumerable` at any time using `#[]`.
96
104
 
97
105
  ```ruby
98
- enum = JsonPath.new('$..color')[object]
106
+ enum = JsonPath.new('$..color')[json]
99
107
  # => #<JsonPath::Enumerable:...>
100
108
  enum.first
101
109
  # => "red"
@@ -103,35 +111,123 @@ enum.any?{ |c| c == 'red' }
103
111
  # => true
104
112
  ```
105
113
 
106
- ### More examples
114
+ For more usage examples and variations on paths, please visit the tests. There
115
+ are some more complex ones as well.
107
116
 
108
- For more usage examples and variations on paths, please visit the tests. There are some more complex ones as well.
117
+ ### Querying ruby data structures
109
118
 
110
- ### Conditional Operators Are Also Supported
119
+ If you have ruby hashes with symbolized keys as input, you
120
+ can use `:use_symbols` to make JsonPath work fine on them too:
111
121
 
112
122
  ```ruby
113
- def test_or_operator
114
- assert_equal [@object['store']['book'][1], @object['store']['book'][3]], JsonPath.new("$..book[?(@['price'] == 13 || @['price'] == 23)]").on(@object)
115
- end
123
+ book = { title: "Sayings of the Century" }
116
124
 
117
- def test_and_operator
118
- assert_equal [], JsonPath.new("$..book[?(@['price'] == 13 && @['price'] == 23)]").on(@object)
119
- end
125
+ JsonPath.new('$.title').on(book)
126
+ # => []
120
127
 
121
- def test_and_operator_with_more_results
122
- assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] < 23 && @['price'] > 9)]").on(@object)
123
- end
128
+ JsonPath.new('$.title', use_symbols: true).on(book)
129
+ # => ["Sayings of the Century"]
124
130
  ```
125
131
 
126
- ### Running an individual test
132
+ JsonPath also recognizes objects responding to `dig` (introduced
133
+ in ruby 2.3), and therefore works out of the box with Struct,
134
+ OpenStruct, and other Hash-like structures:
127
135
 
128
136
  ```ruby
129
- ruby -Ilib:../lib test/test_jsonpath.rb --name test_wildcard_on_intermediary_element_v6
137
+ book_class = Struct.new(:title)
138
+ book = book_class.new("Sayings of the Century")
139
+
140
+ JsonPath.new('$.title').on(book)
141
+ # => ["Sayings of the Century"]
142
+ ```
143
+
144
+ JsonPath is able to query pure ruby objects and uses `__send__`
145
+ on them. The option is enabled by default in JsonPath 1.x, but
146
+ we encourage to enable it explicitly:
147
+
148
+ ```ruby
149
+ book_class = Class.new{ attr_accessor :title }
150
+ book = book_class.new
151
+ book.title = "Sayings of the Century"
152
+
153
+ JsonPath.new('$.title', allow_send: true).on(book)
154
+ # => ["Sayings of the Century"]
155
+ ```
156
+
157
+ ### Other available options
158
+
159
+ By default, JsonPath does not return null values on unexisting paths.
160
+ This can be changed using the `:default_path_leaf_to_null` option
161
+
162
+ ```ruby
163
+ JsonPath.new('$..book[*].isbn').on(json)
164
+ # => ["0-553-21311-3", "0-395-19395-8"]
165
+
166
+ JsonPath.new('$..book[*].isbn', default_path_leaf_to_null: true).on(json)
167
+ # => [nil, nil, "0-553-21311-3", "0-395-19395-8"]
168
+ ```
169
+
170
+ When JsonPath returns a Hash, you can ask to symbolize its keys
171
+ using the `:symbolize_keys` option
172
+
173
+ ```ruby
174
+ JsonPath.new('$..book[0]').on(json)
175
+ # => [{"category" => "reference", ...}]
176
+
177
+ JsonPath.new('$..book[0]', symbolize_keys: true).on(json)
178
+ # => [{category: "reference", ...}]
179
+ ```
180
+
181
+ ### Selecting Values
182
+
183
+ It's possible to select results once a query has been defined after the query. For
184
+ example given this JSON data:
185
+
186
+ ```bash
187
+ {
188
+ "store": {
189
+ "book": [
190
+ {
191
+ "category": "reference",
192
+ "author": "Nigel Rees",
193
+ "title": "Sayings of the Century",
194
+ "price": 8.95
195
+ },
196
+ {
197
+ "category": "fiction",
198
+ "author": "Evelyn Waugh",
199
+ "title": "Sword of Honour",
200
+ "price": 12.99
201
+ }
202
+ ]
203
+ }
204
+ ```
205
+
206
+ ... and this query:
207
+
208
+ ```ruby
209
+ "$.store.book[*](category,author)"
210
+ ```
211
+
212
+ ... the result can be filtered as such:
213
+
214
+ ```bash
215
+ [
216
+ {
217
+ "category" : "reference",
218
+ "author" : "Nigel Rees"
219
+ },
220
+ {
221
+ "category" : "fiction",
222
+ "author" : "Evelyn Waugh"
223
+ }
224
+ ]
130
225
  ```
131
226
 
132
227
  ### Manipulation
133
228
 
134
- If you'd like to do substitution in a json object, you can use `#gsub` or `#gsub!` to modify the object in place.
229
+ If you'd like to do substitution in a json object, you can use `#gsub`
230
+ or `#gsub!` to modify the object in place.
135
231
 
136
232
  ```ruby
137
233
  JsonPath.for('{"candy":"lollipop"}').gsub('$..candy') {|v| "big turks" }.to_hash
@@ -143,7 +239,9 @@ The result will be
143
239
  {'candy' => 'big turks'}
144
240
  ```
145
241
 
146
- If you'd like to remove all nil keys, you can use `#compact` and `#compact!`. To remove all keys under a certain path, use `#delete` or `#delete!`. You can even chain these methods together as follows:
242
+ If you'd like to remove all nil keys, you can use `#compact` and `#compact!`.
243
+ To remove all keys under a certain path, use `#delete` or `#delete!`. You can
244
+ even chain these methods together as follows:
147
245
 
148
246
  ```ruby
149
247
  json = '{"candy":"lollipop","noncandy":null,"other":"things"}'
@@ -155,6 +253,49 @@ o = JsonPath.for(json).
155
253
  # => {"candy" => "big turks"}
156
254
  ```
157
255
 
256
+ ### Fetch all paths
257
+
258
+ To fetch all possible paths in given json, you can use `fetch_all_path`` method.
259
+
260
+ data:
261
+
262
+ ```bash
263
+ {
264
+ "store": {
265
+ "book": [
266
+ {
267
+ "category": "reference",
268
+ "author": "Nigel Rees"
269
+ },
270
+ {
271
+ "category": "fiction",
272
+ "author": "Evelyn Waugh"
273
+ }
274
+ ]
275
+ }
276
+ ```
277
+
278
+ ... and this query:
279
+
280
+ ```ruby
281
+ JsonPath.fetch_all_path(data)
282
+ ```
283
+
284
+ ... the result will be:
285
+
286
+ ```bash
287
+ ["$", "$.store", "$.store.book", "$.store.book[0].category", "$.store.book[0].author", "$.store.book[0]", "$.store.book[1].category", "$.store.book[1].author", "$.store.book[1]"]
288
+ ```
289
+
290
+
291
+
158
292
  # Contributions
159
293
 
160
- Please feel free to submit an Issue or a Pull Request any time you feel like you would like to contribute. Thank you!
294
+ Please feel free to submit an Issue or a Pull Request any time you feel like
295
+ you would like to contribute. Thank you!
296
+
297
+ ## Running an individual test
298
+
299
+ ```ruby
300
+ ruby -Ilib:../lib test/test_jsonpath.rb --name test_wildcard_on_intermediary_element_v6
301
+ ```
data/jsonpath.gemspec CHANGED
@@ -5,10 +5,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'jsonpath', 'version')
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'jsonpath'
7
7
  s.version = JsonPath::VERSION
8
- if s.respond_to? :required_rubygems_version=
9
- s.required_rubygems_version =
10
- Gem::Requirement.new('>= 0')
11
- end
8
+ s.required_ruby_version = '>= 2.6'
12
9
  s.authors = ['Joshua Hull', 'Gergely Brautigam']
13
10
  s.summary = 'Ruby implementation of http://goessner.net/articles/JsonPath/'
14
11
  s.description = 'Ruby implementation of http://goessner.net/articles/JsonPath/.'
@@ -16,20 +13,16 @@ Gem::Specification.new do |s|
16
13
  s.extra_rdoc_files = ['README.md']
17
14
  s.files = `git ls-files`.split("\n")
18
15
  s.homepage = 'https://github.com/joshbuddy/jsonpath'
19
- s.rdoc_options = ['--charset=UTF-8']
20
- s.require_paths = ['lib']
21
- s.rubygems_version = '1.3.7'
22
16
  s.test_files = `git ls-files`.split("\n").select { |f| f =~ /^spec/ }
23
- s.rubyforge_project = 'jsonpath'
24
17
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
25
18
  s.licenses = ['MIT']
26
19
 
27
20
  # dependencies
28
21
  s.add_runtime_dependency 'multi_json'
29
- s.add_runtime_dependency 'to_regexp', '~> 0.2.1'
30
22
  s.add_development_dependency 'bundler'
31
23
  s.add_development_dependency 'code_stats'
32
24
  s.add_development_dependency 'minitest', '~> 2.2.0'
33
25
  s.add_development_dependency 'phocus'
26
+ s.add_development_dependency 'racc'
34
27
  s.add_development_dependency 'rake'
35
28
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class JsonPath
4
+ module Dig
5
+
6
+ # Similar to what Hash#dig or Array#dig
7
+ def dig(context, *keys)
8
+ keys.inject(context){|memo,k|
9
+ dig_one(memo, k)
10
+ }
11
+ end
12
+
13
+ # Returns a hash mapping each key from keys
14
+ # to its dig value on context.
15
+ def dig_as_hash(context, keys)
16
+ keys.each_with_object({}) do |k, memo|
17
+ memo[k] = dig_one(context, k)
18
+ end
19
+ end
20
+
21
+ # Dig the value of k on context.
22
+ def dig_one(context, k)
23
+ case context
24
+ when Hash
25
+ context[@options[:use_symbols] ? k.to_sym : k]
26
+ when Array
27
+ context[k.to_i]
28
+ else
29
+ if context.respond_to?(:dig)
30
+ context.dig(k)
31
+ elsif @options[:allow_send]
32
+ context.__send__(k)
33
+ end
34
+ end
35
+ end
36
+
37
+ # Yields the block if context has a diggable
38
+ # value for k
39
+ def yield_if_diggable(context, k, &blk)
40
+ case context
41
+ when Array
42
+ nil
43
+ when Hash
44
+ k = @options[:use_symbols] ? k.to_sym : k
45
+ return yield if context.key?(k) || @options[:default_path_leaf_to_null]
46
+ else
47
+ if context.respond_to?(:dig)
48
+ digged = dig_one(context, k)
49
+ yield if !digged.nil? || @options[:default_path_leaf_to_null]
50
+ elsif @options[:allow_send] && context.respond_to?(k.to_s) && !Object.respond_to?(k.to_s)
51
+ yield
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ end