jsonpath 1.1.0 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +31 -0
- data/Gemfile +1 -1
- data/README.md +36 -0
- data/jsonpath.gemspec +1 -0
- data/lib/jsonpath/enumerable.rb +0 -3
- data/lib/jsonpath/parser.rb +5 -3
- data/lib/jsonpath/version.rb +1 -1
- data/lib/jsonpath.rb +38 -4
- data/test/test_jsonpath.rb +117 -9
- metadata +18 -4
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab3e08744fe7f8ba3b64670fee8f75a0fa4c9b339de8257f64cdc7ea24f26965
|
4
|
+
data.tar.gz: 447ce47a7c5df36c2fa290da5aafc77304d5e8db9318cfcac2b97dd9ae8c9c8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
data/lib/jsonpath/enumerable.rb
CHANGED
@@ -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
|
data/lib/jsonpath/parser.rb
CHANGED
@@ -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
|
-
|
162
|
+
next_value = bool_or_exp(top[index + 1])
|
163
|
+
res &&= next_value
|
163
164
|
elsif item == '||'
|
164
|
-
|
165
|
+
next_value = bool_or_exp(top[index + 1])
|
166
|
+
res ||= next_value
|
165
167
|
end
|
166
168
|
end
|
167
169
|
|
data/lib/jsonpath/version.rb
CHANGED
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
|
data/test/test_jsonpath.rb
CHANGED
@@ -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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
90
|
-
|
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
|
-
|
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.
|
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:
|
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.
|
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/
|