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 +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/
|