jsonpath 1.1.2 → 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: ab3e08744fe7f8ba3b64670fee8f75a0fa4c9b339de8257f64cdc7ea24f26965
4
- data.tar.gz: 447ce47a7c5df36c2fa290da5aafc77304d5e8db9318cfcac2b97dd9ae8c9c8c
3
+ metadata.gz: ffcdd9076a2bd58ead93c850c8a1ed842a202e6db62314fb2b6c8fd5f8650fb5
4
+ data.tar.gz: 3d6624bd18086aa892f93bff141dd7bf33e516e02c3fd7e8828db13eb3a84436
5
5
  SHA512:
6
- metadata.gz: 5b458262f098ceea595a91a981136141d0ed3f7ef9782d8a205f619be1bb60a7234af11229527ed9fc9ef3baafec323ad33850d87b4dc389ac482c14bb4aa05f
7
- data.tar.gz: d1756aa04d5e07de855439aa0a4895078ab5c3c2c90c4c9ec755ba1da2ffb3fe11e59d1819e03f6a82e8670fd810b1c895da150a9d24176ddf4a86a54705fcbf
6
+ metadata.gz: 492ed8f729cfdb038c2b0303f49bf2bf1bfb85bd4a7d6ae2e37c2b559bf288b73110db65b2c1642f4e1954cde213d10488ab996ad9261c108f253f03aeb41d12
7
+ data.tar.gz: 2642b36da6a171649a9b0dc2e973994a7d3268e71a62ea290e75f91bd7f7af621b0b0dd6d9fb6a1f7148943f2c5ce6965f8de0db69db4041bc973576b7694425
@@ -8,9 +8,11 @@ jobs:
8
8
  fail-fast: false
9
9
  matrix:
10
10
  ruby-version:
11
- - '2.5'
12
11
  - '2.6'
13
12
  - '2.7'
13
+ - '3.2'
14
+ - '3.1'
15
+ - '3.0'
14
16
  - ruby-head
15
17
  - jruby-head
16
18
  - truffleruby-head
data/README.md CHANGED
@@ -255,7 +255,7 @@ o = JsonPath.for(json).
255
255
 
256
256
  ### Fetch all paths
257
257
 
258
- To fetch all possible paths in given json, you can use `fetch_all_paths` method.
258
+ To fetch all possible paths in given json, you can use `fetch_all_path`` method.
259
259
 
260
260
  data:
261
261
 
data/jsonpath.gemspec CHANGED
@@ -5,7 +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
- s.required_ruby_version = '>= 2.5'
8
+ s.required_ruby_version = '>= 2.6'
9
9
  s.authors = ['Joshua Hull', 'Gergely Brautigam']
10
10
  s.summary = 'Ruby implementation of http://goessner.net/articles/JsonPath/'
11
11
  s.description = 'Ruby implementation of http://goessner.net/articles/JsonPath/.'
@@ -21,9 +21,13 @@ class JsonPath
21
21
  when '*', '..', '@'
22
22
  each(context, key, pos + 1, &blk)
23
23
  when '$'
24
- each(context, key, pos + 1, &blk) if node == @object
24
+ if node == @object
25
+ each(context, key, pos + 1, &blk)
26
+ else
27
+ handle_wildcard(node, "['#{expr}']", context, key, pos, &blk)
28
+ end
25
29
  when /^\[(.*)\]$/
26
- handle_wildecard(node, expr, context, key, pos, &blk)
30
+ handle_wildcard(node, expr, context, key, pos, &blk)
27
31
  when /\(.*\)/
28
32
  keys = expr.gsub(/[()]/, '').split(',').map(&:strip)
29
33
  new_context = filter_context(context, keys)
@@ -51,7 +55,7 @@ class JsonPath
51
55
  end
52
56
  end
53
57
 
54
- def handle_wildecard(node, expr, _context, _key, pos, &blk)
58
+ def handle_wildcard(node, expr, _context, _key, pos, &blk)
55
59
  expr[1, expr.size - 2].split(',').each do |sub_path|
56
60
  case sub_path[0]
57
61
  when '\'', '"'
@@ -64,6 +68,7 @@ class JsonPath
64
68
  else
65
69
  next if node.is_a?(Array) && node.empty?
66
70
  next if node.nil? # when default_path_leaf_to_null is true
71
+ next if node.size.zero?
67
72
 
68
73
  array_args = sub_path.split(':')
69
74
  if array_args[0] == '*'
@@ -81,6 +86,7 @@ class JsonPath
81
86
  next unless end_idx
82
87
  next if start_idx == end_idx && start_idx >= node.size
83
88
  end
89
+
84
90
  start_idx %= node.size
85
91
  end_idx %= node.size
86
92
  step = process_function_or_literal(array_args[2], 1)
@@ -148,8 +154,9 @@ class JsonPath
148
154
  return Integer(exp) if exp[0] != '('
149
155
  return nil unless @_current_node
150
156
 
151
- identifiers = /@?((?<!\d)\.(?!\d)(\w+))+/.match(exp)
152
- if !identifiers.nil? && !@_current_node.methods.include?(identifiers[2].to_sym)
157
+ identifiers = /@?(((?<!\d)\.(?!\d)(\w+))|\['(.*?)'\])+/.match(exp)
158
+ # to filter arrays with known/unknown name.
159
+ if (!identifiers.nil? && !(@_current_node.methods.include?(identifiers[2]&.to_sym) || @_current_node.methods.include?(identifiers[4]&.to_sym)))
153
160
  exp_to_eval = exp.dup
154
161
  begin
155
162
  return JsonPath::Parser.new(@_current_node, @options).parse(exp_to_eval)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class JsonPath
4
- VERSION = '1.1.2'
4
+ VERSION = '1.1.5'
5
5
  end
data/lib/jsonpath.rb CHANGED
@@ -12,25 +12,27 @@ require 'jsonpath/parser'
12
12
  # into a token array.
13
13
  class JsonPath
14
14
  PATH_ALL = '$..*'
15
+ MAX_NESTING_ALLOWED = 100
15
16
 
16
17
  DEFAULT_OPTIONS = {
17
18
  :default_path_leaf_to_null => false,
18
19
  :symbolize_keys => false,
19
20
  :use_symbols => false,
20
21
  :allow_send => true,
21
- :max_nesting => 100
22
+ :max_nesting => MAX_NESTING_ALLOWED
22
23
  }
23
24
 
24
25
  attr_accessor :path
25
26
 
26
27
  def initialize(path, opts = {})
27
28
  @opts = DEFAULT_OPTIONS.merge(opts)
29
+ set_max_nesting
28
30
  scanner = StringScanner.new(path.strip)
29
31
  @path = []
30
32
  until scanner.eos?
31
33
  if (token = scanner.scan(/\$\B|@\B|\*|\.\./))
32
34
  @path << token
33
- elsif (token = scanner.scan(/[$@a-zA-Z0-9:{}_-]+/))
35
+ elsif (token = scanner.scan(/[$@\p{Alnum}:{}_ -]+/))
34
36
  @path << "['#{token}']"
35
37
  elsif (token = scanner.scan(/'(.*?)'/))
36
38
  @path << "[#{token}]"
@@ -45,11 +47,9 @@ class JsonPath
45
47
  elsif (token = scanner.scan(/[><=] \d+/))
46
48
  @path.last << token
47
49
  elsif (token = scanner.scan(/./))
48
- begin
49
- @path.last << token
50
- rescue RuntimeError
51
- raise ArgumentError, "character '#{token}' not supported in query"
52
- end
50
+ @path.last << token
51
+ else
52
+ raise ArgumentError, "character '#{scanner.peek(1)}' not supported in query"
53
53
  end
54
54
  end
55
55
  end
@@ -80,14 +80,14 @@ class JsonPath
80
80
 
81
81
  def on(obj_or_str, opts = {})
82
82
  a = enum_on(obj_or_str).to_a
83
- if opts[:symbolize_keys]
83
+ if symbolize_keys?(opts)
84
84
  a.map! do |e|
85
85
  e.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v; }
86
86
  end
87
87
  end
88
88
  a
89
89
  end
90
-
90
+
91
91
  def self.fetch_all_path(obj)
92
92
  all_paths = ['$']
93
93
  find_path(obj, '$', all_paths, obj.class == Array)
@@ -148,4 +148,13 @@ class JsonPath
148
148
  def deep_clone
149
149
  Marshal.load Marshal.dump(self)
150
150
  end
151
+
152
+ def set_max_nesting
153
+ return unless @opts[:max_nesting].is_a?(Integer) && @opts[:max_nesting] > MAX_NESTING_ALLOWED
154
+ @opts[:max_nesting] = false
155
+ end
156
+
157
+ def symbolize_keys?(opts)
158
+ opts.fetch(:symbolize_keys, @opts&.dig(:symbolize_keys))
159
+ end
151
160
  end
@@ -245,7 +245,7 @@ class TestJsonpath < MiniTest::Unit::TestCase
245
245
  end
246
246
 
247
247
  def test_counting
248
- assert_equal 57, JsonPath.new('$..*').on(@object).to_a.size
248
+ assert_equal 59, JsonPath.new('$..*').on(@object).to_a.size
249
249
  end
250
250
 
251
251
  def test_space_in_path
@@ -475,6 +475,16 @@ class TestJsonpath < MiniTest::Unit::TestCase
475
475
  JsonPath.new('$.store._links').on(@object)
476
476
  end
477
477
 
478
+ def test_support_for_umlauts_in_member_names
479
+ assert_equal [@object['store']['Übermorgen']],
480
+ JsonPath.new('$.store.Übermorgen').on(@object)
481
+ end
482
+
483
+ def test_support_for_spaces_in_member_name
484
+ assert_equal [@object['store']['Title Case']],
485
+ JsonPath.new('$.store.Title Case').on(@object)
486
+ end
487
+
478
488
  def test_dig_return_string
479
489
  assert_equal ['asdf'], JsonPath.new("$.store.book..tags[?(@ == 'asdf')]").on(@object)
480
490
  assert_equal [], JsonPath.new("$.store.book..tags[?(@ == 'not_asdf')]").on(@object)
@@ -885,13 +895,17 @@ class TestJsonpath < MiniTest::Unit::TestCase
885
895
  path = "$..book[?((@['author'] == 'Evelyn Waugh' || @['author'] == 'Herman Melville') && (@['price'] == 33 || @['price'] == 9))]"
886
896
  assert_equal [@object['store']['book'][2]], JsonPath.new(path).on(@object)
887
897
  end
888
-
889
- def test_complex_nested_grouping_unmatched_parent
890
- path = "$..book[?((@['author'] == 'Evelyn Waugh' || @['author'] == 'Herman Melville' && (@['price'] == 33 || @['price'] == 9))]"
891
- err = assert_raises(ArgumentError, 'should have raised an exception') { JsonPath.new(path).on(@object) }
892
- assert_match(/unmatched parenthesis in expression: \(\(false \|\| false && \(false \|\| true\)\)/, err.message)
898
+
899
+ def test_nested_with_unknown_key
900
+ path = "$..[?(@.price == 9 || @.price == 33)].title"
901
+ assert_equal ["Sayings of the Century", "Moby Dick", "Sayings of the Century", "Moby Dick"], JsonPath.new(path).on(@object)
893
902
  end
894
903
 
904
+ def test_nested_with_unknown_key_filtered_array
905
+ path = "$..[?(@['price'] == 9 || @['price'] == 33)].title"
906
+ assert_equal ["Sayings of the Century", "Moby Dick", "Sayings of the Century", "Moby Dick"], JsonPath.new(path).on(@object)
907
+ end
908
+
895
909
  def test_runtime_error_frozen_string
896
910
  skip('in ruby version below 2.2.0 this error is not raised') if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.0') || Gem::Version.new(RUBY_VERSION) > Gem::Version::new('2.6')
897
911
  json = '
@@ -1178,6 +1192,10 @@ class TestJsonpath < MiniTest::Unit::TestCase
1178
1192
  assert_raises(MultiJson::ParseError) { JsonPath.new('$.a', max_nesting: 1).on(json) }
1179
1193
  end
1180
1194
 
1195
+ def test_linefeed_in_path_error
1196
+ assert_raises(ArgumentError) { JsonPath.new("$.store\n.book") }
1197
+ end
1198
+
1181
1199
  def test_with_max_nesting_false
1182
1200
  json = {
1183
1201
  a: {
@@ -1191,6 +1209,31 @@ class TestJsonpath < MiniTest::Unit::TestCase
1191
1209
  assert_equal [{}], JsonPath.new('$.a.b.c', max_nesting: false).on(json)
1192
1210
  end
1193
1211
 
1212
+ def test_initialize_with_max_nesting_exceeding_limit
1213
+ json = {
1214
+ a: {
1215
+ b: {
1216
+ c: {
1217
+ }
1218
+ }
1219
+ }
1220
+ }.to_json
1221
+
1222
+ json_obj = JsonPath.new('$.a.b.c', max_nesting: 105)
1223
+ assert_equal [{}], json_obj.on(json)
1224
+ assert_equal false, json_obj.instance_variable_get(:@opts)[:max_nesting]
1225
+ end
1226
+
1227
+ def test_initialize_without_max_nesting_exceeding_limit
1228
+ json_obj = JsonPath.new('$.a.b.c', max_nesting: 90)
1229
+ assert_equal 90, json_obj.instance_variable_get(:@opts)[:max_nesting]
1230
+ end
1231
+
1232
+ def test_initialize_with_max_nesting_false_limit
1233
+ json_obj = JsonPath.new('$.a.b.c', max_nesting: false)
1234
+ assert_equal false, json_obj.instance_variable_get(:@opts)[:max_nesting]
1235
+ end
1236
+
1194
1237
  def example_object
1195
1238
  { 'store' => {
1196
1239
  'book' => [
@@ -1242,10 +1285,12 @@ class TestJsonpath < MiniTest::Unit::TestCase
1242
1285
  },
1243
1286
  '@id' => 'http://example.org/store/42',
1244
1287
  '$meta-data' => 'whatevs',
1288
+ 'Übermorgen' => 'The day after tomorrow',
1289
+ 'Title Case' => 'A title case string',
1245
1290
  '_links' => { 'self' => {} }
1246
1291
  } }
1247
1292
  end
1248
-
1293
+
1249
1294
  def test_fetch_all_path
1250
1295
  data = {
1251
1296
  "foo" => nil,
@@ -1260,4 +1305,17 @@ class TestJsonpath < MiniTest::Unit::TestCase
1260
1305
  }
1261
1306
  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
1307
  end
1308
+
1309
+
1310
+ def test_extractore_with_dollar_key
1311
+ json = {"test" => {"$" =>"success", "a" => "123"}}
1312
+ assert_equal ["success"], JsonPath.on(json, "$.test.$")
1313
+ assert_equal ["123"], JsonPath.on(json, "$.test.a")
1314
+ end
1315
+
1316
+ def test_symbolize_key
1317
+ data = { "store" => { "book" => [{"category" => "reference"}]}}
1318
+ assert_equal [{"category": "reference"}], JsonPath.new('$..book[0]', symbolize_keys: true).on(data)
1319
+ assert_equal [{"category": "reference"}], JsonPath.new('$..book[0]').on(data, symbolize_keys: true)
1320
+ end
1263
1321
  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.2
4
+ version: 1.1.5
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: 2022-04-24 00:00:00.000000000 Z
12
+ date: 2023-10-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -152,14 +152,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
152
  requirements:
153
153
  - - ">="
154
154
  - !ruby/object:Gem::Version
155
- version: '2.5'
155
+ version: '2.6'
156
156
  required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  requirements:
158
158
  - - ">="
159
159
  - !ruby/object:Gem::Version
160
160
  version: '0'
161
161
  requirements: []
162
- rubygems_version: 3.3.7
162
+ rubygems_version: 3.4.1
163
163
  signing_key:
164
164
  specification_version: 4
165
165
  summary: Ruby implementation of http://goessner.net/articles/JsonPath/