jsonpath 1.1.0 → 1.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27b1b5e54ebef03b9cde7f447c1dc6fb8e411b4a28f5039426c87782b250ab1a
4
- data.tar.gz: ae08f25885125bde4f301d905b9c6446bf0e922516aabce101a4e90088fd6e68
3
+ metadata.gz: ab3e08744fe7f8ba3b64670fee8f75a0fa4c9b339de8257f64cdc7ea24f26965
4
+ data.tar.gz: 447ce47a7c5df36c2fa290da5aafc77304d5e8db9318cfcac2b97dd9ae8c9c8c
5
5
  SHA512:
6
- metadata.gz: e4e778c37086407f3fb101b23c7f0507c48f0714ad2ae671d14ce52a2397e18f0f7433b8fa0fe651e503285002b4db1b70dad91ae4de1cf51b948f9ec736818b
7
- data.tar.gz: b0dac82fcb16862190ae1405680d4779d6b3208d6ad31e25e5b0eb800877ca8bf56bae2789bac11452dfd872593071334205208bcb3a7320e3a8c0b94ba206c9
6
+ metadata.gz: 5b458262f098ceea595a91a981136141d0ed3f7ef9782d8a205f619be1bb60a7234af11229527ed9fc9ef3baafec323ad33850d87b4dc389ac482c14bb4aa05f
7
+ data.tar.gz: d1756aa04d5e07de855439aa0a4895078ab5c3c2c90c4c9ec755ba1da2ffb3fe11e59d1819e03f6a82e8670fd810b1c895da150a9d24176ddf4a86a54705fcbf
@@ -0,0 +1,31 @@
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.5'
12
+ - '2.6'
13
+ - '2.7'
14
+ - ruby-head
15
+ - jruby-head
16
+ - truffleruby-head
17
+ runs-on:
18
+ - ubuntu-latest
19
+
20
+ runs-on: ${{ matrix.runs-on }}
21
+
22
+ steps:
23
+
24
+ - uses: actions/checkout@v2
25
+
26
+ - uses: ruby/setup-ruby@v1
27
+ with:
28
+ ruby-version: ${{ matrix.ruby-version }}
29
+ bundler-cache: true
30
+
31
+ - run: bundle exec rake test
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
@@ -253,6 +253,42 @@ o = JsonPath.for(json).
253
253
  # => {"candy" => "big turks"}
254
254
  ```
255
255
 
256
+ ### Fetch all paths
257
+
258
+ To fetch all possible paths in given json, you can use `fetch_all_paths` 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
+
256
292
  # Contributions
257
293
 
258
294
  Please feel free to submit an Issue or a Pull Request any time you feel like
data/jsonpath.gemspec CHANGED
@@ -23,5 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency 'code_stats'
24
24
  s.add_development_dependency 'minitest', '~> 2.2.0'
25
25
  s.add_development_dependency 'phocus'
26
+ s.add_development_dependency 'racc'
26
27
  s.add_development_dependency 'rake'
27
28
  end
@@ -151,9 +151,6 @@ class JsonPath
151
151
  identifiers = /@?((?<!\d)\.(?!\d)(\w+))+/.match(exp)
152
152
  if !identifiers.nil? && !@_current_node.methods.include?(identifiers[2].to_sym)
153
153
  exp_to_eval = exp.dup
154
- exp_to_eval[identifiers[0]] = identifiers[0].split('.').map do |el|
155
- el == '@' ? '@' : "['#{el}']"
156
- end.join
157
154
  begin
158
155
  return JsonPath::Parser.new(@_current_node, @options).parse(exp_to_eval)
159
156
  rescue StandardError
@@ -68,7 +68,7 @@ class JsonPath
68
68
  scanner = StringScanner.new(exp)
69
69
  elements = []
70
70
  until scanner.eos?
71
- if (t = scanner.scan(/\['[a-zA-Z@&*\/$%^?_]+'\]|\.[a-zA-Z0-9_]+[?!]?/))
71
+ if (t = scanner.scan(/\['[a-zA-Z@&*\/$%^?_]+'\]|\.[a-zA-Z0-9_]+[?]?/))
72
72
  elements << t.gsub(/[\[\]'.]|\s+/, '')
73
73
  elsif (t = scanner.scan(/(\s+)?[<>=!\-+][=~]?(\s+)?/))
74
74
  operator = t
@@ -159,9 +159,11 @@ class JsonPath
159
159
  res = bool_or_exp(top.shift)
160
160
  top.each_with_index do |item, index|
161
161
  if item == '&&'
162
- res &&= top[index + 1]
162
+ next_value = bool_or_exp(top[index + 1])
163
+ res &&= next_value
163
164
  elsif item == '||'
164
- res ||= top[index + 1]
165
+ next_value = bool_or_exp(top[index + 1])
166
+ res ||= next_value
165
167
  end
166
168
  end
167
169
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class JsonPath
4
- VERSION = '1.1.0'
4
+ VERSION = '1.1.2'
5
5
  end
data/lib/jsonpath.rb CHANGED
@@ -17,7 +17,8 @@ class JsonPath
17
17
  :default_path_leaf_to_null => false,
18
18
  :symbolize_keys => false,
19
19
  :use_symbols => false,
20
- :allow_send => true
20
+ :allow_send => true,
21
+ :max_nesting => 100
21
22
  }
22
23
 
23
24
  attr_accessor :path
@@ -86,13 +87,46 @@ class JsonPath
86
87
  end
87
88
  a
88
89
  end
90
+
91
+ def self.fetch_all_path(obj)
92
+ all_paths = ['$']
93
+ find_path(obj, '$', all_paths, obj.class == Array)
94
+ return all_paths
95
+ end
96
+
97
+ def self.find_path(obj, root_key, all_paths, is_array = false)
98
+ obj.each do |key, value|
99
+ table_params = { key: key, root_key: root_key}
100
+ is_loop = value.class == Array || value.class == Hash
101
+ if is_loop
102
+ path_exp = construct_path(table_params)
103
+ all_paths << path_exp
104
+ find_path(value, path_exp, all_paths, value.class == Array)
105
+ elsif is_array
106
+ table_params[:index] = obj.find_index(key)
107
+ path_exp = construct_path(table_params)
108
+ find_path(key, path_exp, all_paths, key.class == Array) if key.class == Hash || key.class == Array
109
+ all_paths << path_exp
110
+ else
111
+ all_paths << construct_path(table_params)
112
+ end
113
+ end
114
+ end
115
+
116
+ def self.construct_path(table_row)
117
+ if table_row[:index]
118
+ return table_row[:root_key] + '['+ table_row[:index].to_s + ']'
119
+ else
120
+ return table_row[:root_key] + '.'+ table_row[:key]
121
+ end
122
+ end
89
123
 
90
124
  def first(obj_or_str, *args)
91
125
  enum_on(obj_or_str).first(*args)
92
126
  end
93
127
 
94
128
  def enum_on(obj_or_str, mode = nil)
95
- JsonPath::Enumerable.new(self, self.class.process_object(obj_or_str), mode,
129
+ JsonPath::Enumerable.new(self, self.class.process_object(obj_or_str, @opts), mode,
96
130
  @opts)
97
131
  end
98
132
  alias [] enum_on
@@ -107,8 +141,8 @@ class JsonPath
107
141
 
108
142
  private
109
143
 
110
- def self.process_object(obj_or_str)
111
- obj_or_str.is_a?(String) ? MultiJson.decode(obj_or_str) : obj_or_str
144
+ def self.process_object(obj_or_str, opts = {})
145
+ obj_or_str.is_a?(String) ? MultiJson.decode(obj_or_str, max_nesting: opts[:max_nesting]) : obj_or_str
112
146
  end
113
147
 
114
148
  def deep_clone
@@ -70,24 +70,60 @@ class TestJsonpath < MiniTest::Unit::TestCase
70
70
  assert_equal [@object['store']['book'][0], @object['store']['book'][2]], JsonPath.new("$..book[?(@['price'] < 10)]").on(@object)
71
71
  assert_equal [@object['store']['book'][0], @object['store']['book'][2]], JsonPath.new("$..book[?(@['price'] == 9)]").on(@object)
72
72
  assert_equal [@object['store']['book'][3]], JsonPath.new("$..book[?(@['price'] > 20)]").on(@object)
73
- assert_equal [
74
- @object['store']['book'][0],
75
- @object['store']['book'][4],
76
- @object['store']['book'][5],
77
- @object['store']['book'][6]
78
- ], JsonPath.new("$..book[?(@['category'] != 'fiction')]").on(@object)
73
+ end
74
+
75
+ def test_not_equals_operator
76
+ expected =
77
+ [
78
+ @object['store']['book'][0],
79
+ @object['store']['book'][4],
80
+ @object['store']['book'][5],
81
+ @object['store']['book'][6]
82
+ ]
83
+ assert_equal(expected, JsonPath.new("$..book[?(@['category'] != 'fiction')]").on(@object))
84
+ assert_equal(expected, JsonPath.new("$..book[?(@['category']!=fiction)]").on(@object))
85
+ assert_equal(expected, JsonPath.new("$..book[?(@.category!=fiction)]").on(@object))
86
+ assert_equal(expected, JsonPath.new("$..book[?(@.category != 'fiction')]").on(@object))
79
87
  end
80
88
 
81
89
  def test_or_operator
82
90
  assert_equal [@object['store']['book'][1], @object['store']['book'][3]], JsonPath.new("$..book[?(@['price'] == 13 || @['price'] == 23)]").on(@object)
91
+ result = ["Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings"]
92
+ assert_equal result, JsonPath.new("$..book[?(@.price==13 || @.price==9 || @.price==23)].title").on(@object)
93
+ assert_equal result, JsonPath.new("$..book[?(@.price==9 || @.price==23 || @.price==13)].title").on(@object)
94
+ assert_equal result, JsonPath.new("$..book[?(@.price==23 || @.price==13 || @.price==9)].title").on(@object)
95
+ end
96
+
97
+ def test_or_operator_with_not_equals
98
+ # Should be the same regardless of key style ( @.key vs @['key'] )
99
+ result = ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien', 'Lukyanenko']
100
+ assert_equal result, JsonPath.new("$..book[?(@['title']=='Osennie Vizity' || @['author']!='Lukyanenko')].author").on(@object)
101
+ assert_equal result, JsonPath.new("$..book[?(@.title=='Osennie Vizity' || @.author != Lukyanenko )].author").on(@object)
102
+ assert_equal result, JsonPath.new("$..book[?(@.title=='Osennie Vizity' || @.author!=Lukyanenko )].author").on(@object)
83
103
  end
84
104
 
85
105
  def test_and_operator
86
106
  assert_equal [], JsonPath.new("$..book[?(@['price'] == 13 && @['price'] == 23)]").on(@object)
107
+ assert_equal [], JsonPath.new("$..book[?(@.price>=13 && @.category==fiction && @.title==no_match)]").on(@object)
108
+ assert_equal [], JsonPath.new("$..book[?(@.title==no_match && @.category==fiction && @.price==13)]").on(@object)
109
+ assert_equal [], JsonPath.new("$..book[?(@.price==13 && @.title==no_match && @.category==fiction)]").on(@object)
110
+ assert_equal [], JsonPath.new("$..book[?(@.price==13 && @.bad_key_name==true && @.category==fiction)]").on(@object)
111
+
112
+ expected = [@object['store']['book'][1]]
113
+ assert_equal expected, JsonPath.new("$..book[?(@['price'] < 23 && @['price'] > 9)]").on(@object)
114
+ assert_equal expected, JsonPath.new("$..book[?(@.price < 23 && @.price > 9)]").on(@object)
115
+
116
+ expected = ['Sword of Honour', 'The Lord of the Rings']
117
+ assert_equal expected, JsonPath.new("$..book[?(@.price>=13 && @.category==fiction)].title").on(@object)
118
+ assert_equal ['The Lord of the Rings'], JsonPath.new("$..book[?(@.category==fiction && @.isbn && @.price>9)].title").on(@object)
119
+ assert_equal ['Sayings of the Century'], JsonPath.new("$..book[?(@['price'] == 9 && @.author=='Nigel Rees')].title").on(@object)
120
+ assert_equal ['Sayings of the Century'], JsonPath.new("$..book[?(@['price'] == 9 && @.tags..asdf)].title").on(@object)
87
121
  end
88
122
 
89
- def test_and_operator_with_more_results
90
- assert_equal [@object['store']['book'][1]], JsonPath.new("$..book[?(@['price'] < 23 && @['price'] > 9)]").on(@object)
123
+ def test_and_operator_with_not_equals
124
+ expected = ['Nigel Rees']
125
+ assert_equal expected, JsonPath.new("$..book[?(@['price']==9 && @['category']!=fiction)].author").on(@object)
126
+ assert_equal expected, JsonPath.new("$..book[?(@.price==9 && @.category!=fiction)].author").on(@object)
91
127
  end
92
128
 
93
129
  def test_nested_grouping
@@ -570,11 +606,42 @@ class TestJsonpath < MiniTest::Unit::TestCase
570
606
  'name' => 'testname2'
571
607
  }]
572
608
  }
573
- assert_equal [{ 'isTrue' => true, 'name' => 'testname1' }], JsonPath.new('$.data[?(@.isTrue)]').on(data)
609
+
610
+ # These queries should be equivalent
611
+ expected = [{ 'isTrue' => true, 'name' => 'testname1' }]
612
+ assert_equal expected, JsonPath.new('$.data[?(@.isTrue)]').on(data)
613
+ assert_equal expected, JsonPath.new('$.data[?(@.isTrue==true)]').on(data)
614
+ assert_equal expected, JsonPath.new('$.data[?(@.isTrue == true)]').on(data)
615
+
616
+ # These queries should be equivalent
617
+ expected = [{ 'isTrue' => false, 'name' => 'testname2' }]
618
+ assert_equal expected, JsonPath.new('$.data[?(@.isTrue != true)]').on(data)
619
+ assert_equal expected, JsonPath.new('$.data[?(@.isTrue!=true)]').on(data)
620
+ assert_equal expected, JsonPath.new('$.data[?(@.isTrue==false)]').on(data)
621
+ end
622
+
623
+ def test_and_operator_with_boolean_parameter_value
624
+ data = {
625
+ 'data' => [{
626
+ 'hasProperty1' => true,
627
+ 'hasProperty2' => false,
628
+ 'name' => 'testname1'
629
+ }, {
630
+ 'hasProperty1' => false,
631
+ 'hasProperty2' => true,
632
+ 'name' => 'testname2'
633
+ }, {
634
+ 'hasProperty1' => true,
635
+ 'hasProperty2' => true,
636
+ 'name' => 'testname3'
637
+ }]
638
+ }
639
+ assert_equal ['testname3'], JsonPath.new('$.data[?(@.hasProperty1 && @.hasProperty2)].name').on(data)
574
640
  end
575
641
 
576
642
  def test_regex_simple
577
643
  assert_equal %w[asdf asdf2], JsonPath.new('$.store.book..tags[?(@ =~ /asdf/)]').on(@object)
644
+ assert_equal %w[asdf asdf2], JsonPath.new('$.store.book..tags[?(@=~/asdf/)]').on(@object)
578
645
  end
579
646
 
580
647
  def test_regex_simple_miss
@@ -1098,6 +1165,32 @@ class TestJsonpath < MiniTest::Unit::TestCase
1098
1165
  assert_equal [12, nil, nil], JsonPath.new('$.bars[*].foo', default_path_leaf_to_null: true).on(data)
1099
1166
  end
1100
1167
 
1168
+ def test_raise_max_nesting_error
1169
+ json = {
1170
+ a: {
1171
+ b: {
1172
+ c: {
1173
+ }
1174
+ }
1175
+ }
1176
+ }.to_json
1177
+
1178
+ assert_raises(MultiJson::ParseError) { JsonPath.new('$.a', max_nesting: 1).on(json) }
1179
+ end
1180
+
1181
+ def test_with_max_nesting_false
1182
+ json = {
1183
+ a: {
1184
+ b: {
1185
+ c: {
1186
+ }
1187
+ }
1188
+ }
1189
+ }.to_json
1190
+
1191
+ assert_equal [{}], JsonPath.new('$.a.b.c', max_nesting: false).on(json)
1192
+ end
1193
+
1101
1194
  def example_object
1102
1195
  { 'store' => {
1103
1196
  'book' => [
@@ -1152,4 +1245,19 @@ class TestJsonpath < MiniTest::Unit::TestCase
1152
1245
  '_links' => { 'self' => {} }
1153
1246
  } }
1154
1247
  end
1248
+
1249
+ def test_fetch_all_path
1250
+ data = {
1251
+ "foo" => nil,
1252
+ "bar" => {
1253
+ "baz" => nil
1254
+ },
1255
+ "bars" => [
1256
+ { "foo" => 12 },
1257
+ { "foo" => nil },
1258
+ { }
1259
+ ]
1260
+ }
1261
+ assert_equal ["$", "$.foo", "$.bar", "$.bar.baz", "$.bars", "$.bars[0].foo", "$.bars[0]", "$.bars[1].foo", "$.bars[1]", "$.bars[2]"], JsonPath.fetch_all_path(data)
1262
+ end
1155
1263
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonpath
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Hull
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-28 00:00:00.000000000 Z
12
+ date: 2022-04-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -81,6 +81,20 @@ dependencies:
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: racc
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
84
98
  - !ruby/object:Gem::Dependency
85
99
  name: rake
86
100
  requirement: !ruby/object:Gem::Requirement
@@ -106,11 +120,11 @@ extra_rdoc_files:
106
120
  - README.md
107
121
  files:
108
122
  - ".gemtest"
123
+ - ".github/workflows/test.yml"
109
124
  - ".gitignore"
110
125
  - ".rspec"
111
126
  - ".rubocop.yml"
112
127
  - ".rubocop_todo.yml"
113
- - ".travis.yml"
114
128
  - Gemfile
115
129
  - LICENSE.md
116
130
  - README.md
@@ -145,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
159
  - !ruby/object:Gem::Version
146
160
  version: '0'
147
161
  requirements: []
148
- rubygems_version: 3.1.2
162
+ rubygems_version: 3.3.7
149
163
  signing_key:
150
164
  specification_version: 4
151
165
  summary: Ruby implementation of http://goessner.net/articles/JsonPath/
data/.travis.yml DELETED
@@ -1,8 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.5
4
- - 2.6
5
- - 2.7
6
- - ruby-head
7
- - jruby-head
8
- - truffleruby-head