jsonpath 1.0.2 → 1.0.7
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/.gitignore +3 -0
- data/.travis.yml +4 -7
- data/README.md +45 -0
- data/jsonpath.gemspec +1 -9
- data/lib/jsonpath.rb +13 -11
- data/lib/jsonpath/enumerable.rb +17 -2
- data/lib/jsonpath/parser.rb +60 -23
- data/lib/jsonpath/proxy.rb +2 -2
- data/lib/jsonpath/version.rb +1 -1
- data/test/test_jsonpath.rb +145 -4
- metadata +5 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a31bb2922acb2b1a6e84a6e640ee148eb1f40f9d8113751c6903149e472e99d9
|
4
|
+
data.tar.gz: ab119c28fe07e14bfab16e5e2905d59ccf7e8154d5ff817d14a9db92215f8bd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ede5f5c546e8ac46689b993ada2d6ed456ed9e9b1027023e421b8d27e99b3f785360a3fcf093a58be473a80ecca3485b709f23575be6b93fb71c2ae46f63966
|
7
|
+
data.tar.gz: 021a544e520b944266c276b3b79ca53e0311df7ade212655282e4f3f03b9bca18aba5858148dc7de8ec3ca91be67dbd2f5554c4b6a73273c094d94dc31f5aa71
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -123,6 +123,51 @@ For more usage examples and variations on paths, please visit the tests. There a
|
|
123
123
|
end
|
124
124
|
```
|
125
125
|
|
126
|
+
### Selecting Values
|
127
|
+
|
128
|
+
It's possible to select results once a query has been defined after the query. For example given this JSON data:
|
129
|
+
|
130
|
+
```bash
|
131
|
+
{
|
132
|
+
"store": {
|
133
|
+
"book": [
|
134
|
+
{
|
135
|
+
"category": "reference",
|
136
|
+
"author": "Nigel Rees",
|
137
|
+
"title": "Sayings of the Century",
|
138
|
+
"price": 8.95
|
139
|
+
},
|
140
|
+
{
|
141
|
+
"category": "fiction",
|
142
|
+
"author": "Evelyn Waugh",
|
143
|
+
"title": "Sword of Honour",
|
144
|
+
"price": 12.99
|
145
|
+
}
|
146
|
+
]
|
147
|
+
}
|
148
|
+
```
|
149
|
+
|
150
|
+
... and this query:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
"$.store.book[*](category,author)"
|
154
|
+
```
|
155
|
+
|
156
|
+
... the result can be filtered as such:
|
157
|
+
|
158
|
+
```bash
|
159
|
+
[
|
160
|
+
{
|
161
|
+
"category" : "reference",
|
162
|
+
"author" : "Nigel Rees"
|
163
|
+
},
|
164
|
+
{
|
165
|
+
"category" : "fiction",
|
166
|
+
"author" : "Evelyn Waugh"
|
167
|
+
}
|
168
|
+
]
|
169
|
+
```
|
170
|
+
|
126
171
|
### Running an individual test
|
127
172
|
|
128
173
|
```ruby
|
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
|
-
|
9
|
-
s.required_rubygems_version =
|
10
|
-
Gem::Requirement.new('>= 0')
|
11
|
-
end
|
8
|
+
s.required_ruby_version = '>= 2.5'
|
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,17 +13,12 @@ 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'
|
data/lib/jsonpath.rb
CHANGED
@@ -10,7 +10,7 @@ require 'jsonpath/parser'
|
|
10
10
|
# JsonPath: initializes the class with a given JsonPath and parses that path
|
11
11
|
# into a token array.
|
12
12
|
class JsonPath
|
13
|
-
PATH_ALL = '$..*'
|
13
|
+
PATH_ALL = '$..*'
|
14
14
|
|
15
15
|
attr_accessor :path
|
16
16
|
|
@@ -19,21 +19,23 @@ class JsonPath
|
|
19
19
|
scanner = StringScanner.new(path.strip)
|
20
20
|
@path = []
|
21
21
|
until scanner.eos?
|
22
|
-
if token = scanner.scan(/\$\B|@\B|\*|\.\./)
|
22
|
+
if (token = scanner.scan(/\$\B|@\B|\*|\.\./))
|
23
23
|
@path << token
|
24
|
-
elsif token = scanner.scan(/[
|
24
|
+
elsif (token = scanner.scan(/[$@a-zA-Z0-9:{}_-]+/))
|
25
25
|
@path << "['#{token}']"
|
26
|
-
elsif token = scanner.scan(/'(.*?)'/)
|
26
|
+
elsif (token = scanner.scan(/'(.*?)'/))
|
27
27
|
@path << "[#{token}]"
|
28
|
-
elsif token = scanner.scan(/\[/)
|
28
|
+
elsif (token = scanner.scan(/\[/))
|
29
29
|
@path << find_matching_brackets(token, scanner)
|
30
|
-
elsif token = scanner.scan(/\]/)
|
30
|
+
elsif (token = scanner.scan(/\]/))
|
31
31
|
raise ArgumentError, 'unmatched closing bracket'
|
32
|
+
elsif (token = scanner.scan(/\(.*\)/))
|
33
|
+
@path << token
|
32
34
|
elsif scanner.scan(/\./)
|
33
35
|
nil
|
34
|
-
elsif token = scanner.scan(/[><=] \d+/)
|
36
|
+
elsif (token = scanner.scan(/[><=] \d+/))
|
35
37
|
@path.last << token
|
36
|
-
elsif token = scanner.scan(/./)
|
38
|
+
elsif (token = scanner.scan(/./))
|
37
39
|
begin
|
38
40
|
@path.last << token
|
39
41
|
rescue RuntimeError
|
@@ -46,13 +48,13 @@ class JsonPath
|
|
46
48
|
def find_matching_brackets(token, scanner)
|
47
49
|
count = 1
|
48
50
|
until count.zero?
|
49
|
-
if t = scanner.scan(/\[/)
|
51
|
+
if (t = scanner.scan(/\[/))
|
50
52
|
token << t
|
51
53
|
count += 1
|
52
|
-
elsif t = scanner.scan(/\]/)
|
54
|
+
elsif (t = scanner.scan(/\]/))
|
53
55
|
token << t
|
54
56
|
count -= 1
|
55
|
-
elsif t = scanner.scan(/[^\[\]]+/)
|
57
|
+
elsif (t = scanner.scan(/[^\[\]]+/))
|
56
58
|
token << t
|
57
59
|
elsif scanner.eos?
|
58
60
|
raise ArgumentError, 'unclosed bracket'
|
data/lib/jsonpath/enumerable.rb
CHANGED
@@ -28,6 +28,10 @@ class JsonPath
|
|
28
28
|
each(context, key, pos + 1, &blk) if node == @object
|
29
29
|
when /^\[(.*)\]$/
|
30
30
|
handle_wildecard(node, expr, context, key, pos, &blk)
|
31
|
+
when /\(.*\)/
|
32
|
+
keys = expr.gsub(/[()]/, '').split(',').map(&:strip)
|
33
|
+
new_context = filter_context(context, keys)
|
34
|
+
yield_value(blk, new_context, key)
|
31
35
|
end
|
32
36
|
|
33
37
|
if pos > 0 && @path[pos - 1] == '..' || (@path[pos - 1] == '*' && @path[pos] != '..')
|
@@ -40,6 +44,18 @@ class JsonPath
|
|
40
44
|
|
41
45
|
private
|
42
46
|
|
47
|
+
def filter_context(context, keys)
|
48
|
+
case context
|
49
|
+
when Hash
|
50
|
+
# TODO: Change this to `slice(*keys)` when ruby version support is > 2.4
|
51
|
+
context.select { |k| keys.include?(k) }
|
52
|
+
when Array
|
53
|
+
context.each_with_object([]) do |c, memo|
|
54
|
+
memo << c.select { |k| keys.include?(k) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
43
59
|
def handle_wildecard(node, expr, _context, _key, pos, &blk)
|
44
60
|
expr[1, expr.size - 2].split(',').each do |sub_path|
|
45
61
|
case sub_path[0]
|
@@ -48,7 +64,7 @@ class JsonPath
|
|
48
64
|
if node.is_a?(Hash)
|
49
65
|
node[k] ||= nil if @options[:default_path_leaf_to_null]
|
50
66
|
each(node, k, pos + 1, &blk) if node.key?(k)
|
51
|
-
elsif node.respond_to?(k.to_s)
|
67
|
+
elsif node.respond_to?(k.to_s) && !Object.respond_to?(k.to_s)
|
52
68
|
each(node, k, pos + 1, &blk)
|
53
69
|
end
|
54
70
|
when '?'
|
@@ -112,7 +128,6 @@ class JsonPath
|
|
112
128
|
end
|
113
129
|
|
114
130
|
def yield_value(blk, context, key)
|
115
|
-
key = Integer(key) rescue key if key
|
116
131
|
case @mode
|
117
132
|
when nil
|
118
133
|
blk.call(key ? context[key] : context)
|
data/lib/jsonpath/parser.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'strscan'
|
4
|
-
require 'to_regexp'
|
5
4
|
|
6
5
|
class JsonPath
|
7
6
|
# Parser parses and evaluates an expression passed to @_current_node.
|
8
7
|
class Parser
|
8
|
+
REGEX = /\A\/(.+)\/([imxnesu]*)\z|\A%r{(.+)}([imxnesu]*)\z/
|
9
|
+
|
9
10
|
def initialize(node)
|
10
11
|
@_current_node = node
|
11
12
|
@_expr_map = {}
|
@@ -46,29 +47,43 @@ class JsonPath
|
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
|
-
#
|
50
|
+
# Using a scanner break down the individual expressions and determine if
|
50
51
|
# there is a match in the JSON for it or not.
|
51
52
|
def parse_exp(exp)
|
52
53
|
exp = exp.sub(/@/, '').gsub(/^\(/, '').gsub(/\)$/, '').tr('"', '\'').strip
|
54
|
+
exp.scan(/^\[(\d+)\]/) do |i|
|
55
|
+
next if i.empty?
|
56
|
+
|
57
|
+
index = Integer(i[0])
|
58
|
+
raise ArgumentError, 'Node does not appear to be an array.' unless @_current_node.is_a?(Array)
|
59
|
+
raise ArgumentError, "Index out of bounds for nested array. Index: #{index}" if @_current_node.size < index
|
60
|
+
|
61
|
+
@_current_node = @_current_node[index]
|
62
|
+
# Remove the extra '' and the index.
|
63
|
+
exp = exp.gsub(/^\[\d+\]|\[''\]/, '')
|
64
|
+
end
|
53
65
|
scanner = StringScanner.new(exp)
|
54
66
|
elements = []
|
55
67
|
until scanner.eos?
|
56
|
-
if t = scanner.scan(/\['[a-zA-Z
|
57
|
-
elements << t.gsub(/[\[\]'
|
58
|
-
elsif t = scanner.scan(/(\s+)?[<>=!\-+][=~]?(\s+)?/)
|
68
|
+
if (t = scanner.scan(/\['[a-zA-Z@&*\/$%^?_]+'\]|\.[a-zA-Z0-9_]+[?!]?/))
|
69
|
+
elements << t.gsub(/[\[\]'.]|\s+/, '')
|
70
|
+
elsif (t = scanner.scan(/(\s+)?[<>=!\-+][=~]?(\s+)?/))
|
59
71
|
operator = t
|
60
|
-
elsif t = scanner.scan(/(\s+)?'?.*'?(\s+)?/)
|
72
|
+
elsif (t = scanner.scan(/(\s+)?'?.*'?(\s+)?/))
|
61
73
|
# If we encounter a node which does not contain `'` it means
|
62
74
|
# that we are dealing with a boolean type.
|
63
|
-
operand =
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
operand =
|
76
|
+
if t == 'true'
|
77
|
+
true
|
78
|
+
elsif t == 'false'
|
79
|
+
false
|
80
|
+
elsif operator.to_s.strip == '=~'
|
81
|
+
parse_regex(t)
|
82
|
+
else
|
83
|
+
t.gsub(%r{^'|'$}, '').strip
|
84
|
+
end
|
85
|
+
elsif (t = scanner.scan(/\/\w+\//))
|
86
|
+
elsif (t = scanner.scan(/.*/))
|
72
87
|
raise "Could not process symbol: #{t}"
|
73
88
|
end
|
74
89
|
end
|
@@ -78,9 +93,7 @@ class JsonPath
|
|
78
93
|
elsif @_current_node.is_a?(Hash)
|
79
94
|
@_current_node.dig(*elements)
|
80
95
|
else
|
81
|
-
elements.inject(@_current_node
|
82
|
-
agg.__send__(key)
|
83
|
-
end
|
96
|
+
elements.inject(@_current_node, &:__send__)
|
84
97
|
end
|
85
98
|
|
86
99
|
return (el ? true : false) if el.nil? || operator.nil?
|
@@ -93,6 +106,31 @@ class JsonPath
|
|
93
106
|
|
94
107
|
private
|
95
108
|
|
109
|
+
# /foo/i -> Regex.new("foo", Regexp::IGNORECASE) without using eval
|
110
|
+
# also supports %r{foo}i
|
111
|
+
# following https://github.com/seamusabshere/to_regexp/blob/master/lib/to_regexp.rb
|
112
|
+
def parse_regex(t)
|
113
|
+
t =~ REGEX
|
114
|
+
content = $1 || $3
|
115
|
+
options = $2 || $4
|
116
|
+
|
117
|
+
raise ArgumentError, "unsupported regex #{t} use /foo/ style" if !content || !options
|
118
|
+
|
119
|
+
content = content.gsub '\\/', '/'
|
120
|
+
|
121
|
+
flags = 0
|
122
|
+
flags |= Regexp::IGNORECASE if options.include?('i')
|
123
|
+
flags |= Regexp::MULTILINE if options.include?('m')
|
124
|
+
flags |= Regexp::EXTENDED if options.include?('x')
|
125
|
+
|
126
|
+
# 'n' = none, 'e' = EUC, 's' = SJIS, 'u' = UTF-8
|
127
|
+
lang = options.scan(/[nes]/).join.downcase # ignores u since that is default and causes a warning
|
128
|
+
|
129
|
+
args = [content, flags]
|
130
|
+
args << lang unless lang.empty? # avoid warning
|
131
|
+
Regexp.new(*args)
|
132
|
+
end
|
133
|
+
|
96
134
|
# This will break down a parenthesis from the left to the right
|
97
135
|
# and replace the given expression with it's returned value.
|
98
136
|
# It does this in order to make it easy to eliminate groups
|
@@ -117,10 +155,9 @@ class JsonPath
|
|
117
155
|
top = top.map(&:strip)
|
118
156
|
res = bool_or_exp(top.shift)
|
119
157
|
top.each_with_index do |item, index|
|
120
|
-
|
121
|
-
when '&&'
|
158
|
+
if item == '&&'
|
122
159
|
res &&= top[index + 1]
|
123
|
-
|
160
|
+
elsif item == '||'
|
124
161
|
res ||= top[index + 1]
|
125
162
|
end
|
126
163
|
end
|
@@ -129,9 +166,9 @@ class JsonPath
|
|
129
166
|
# and the closing index will be the last index. To avoid
|
130
167
|
# off-by-one errors we simply return the result at that point.
|
131
168
|
if closing_index + 1 >= str.length && opening_index == 0
|
132
|
-
|
169
|
+
res.to_s
|
133
170
|
else
|
134
|
-
|
171
|
+
"#{str[0..opening_index - 1]}#{res}#{str[closing_index + 1..str.length]}"
|
135
172
|
end
|
136
173
|
end
|
137
174
|
|
data/lib/jsonpath/proxy.rb
CHANGED
@@ -10,11 +10,11 @@ class JsonPath
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def gsub(path, replacement = nil, &replacement_block)
|
13
|
-
_gsub(_deep_copy, path, replacement ? proc
|
13
|
+
_gsub(_deep_copy, path, replacement ? proc(&method(:replacement)) : replacement_block)
|
14
14
|
end
|
15
15
|
|
16
16
|
def gsub!(path, replacement = nil, &replacement_block)
|
17
|
-
_gsub(@obj, path, replacement ? proc
|
17
|
+
_gsub(@obj, path, replacement ? proc(&method(:replacement)) : replacement_block)
|
18
18
|
end
|
19
19
|
|
20
20
|
def delete(path = JsonPath::PATH_ALL)
|
data/lib/jsonpath/version.rb
CHANGED
data/test/test_jsonpath.rb
CHANGED
@@ -511,15 +511,31 @@ class TestJsonpath < MiniTest::Unit::TestCase
|
|
511
511
|
assert_equal [{ 'isTrue' => true, 'name' => 'testname1' }], JsonPath.new('$.data[?(@.isTrue)]').on(data)
|
512
512
|
end
|
513
513
|
|
514
|
-
def
|
515
|
-
assert_equal [], JsonPath.new('
|
514
|
+
def test_regex_simple
|
515
|
+
assert_equal %w[asdf asdf2], JsonPath.new('$.store.book..tags[?(@ =~ /asdf/)]').on(@object)
|
516
|
+
end
|
517
|
+
|
518
|
+
def test_regex_simple_miss
|
519
|
+
assert_equal [], JsonPath.new('$.store.book..tags[?(@ =~ /wut/)]').on(@object)
|
520
|
+
end
|
521
|
+
|
522
|
+
def test_regex_r
|
523
|
+
assert_equal %w[asdf asdf2], JsonPath.new('$.store.book..tags[?(@ =~ %r{asdf})]').on(@object)
|
524
|
+
end
|
525
|
+
|
526
|
+
def test_regex_flags
|
516
527
|
assert_equal [
|
517
528
|
@object['store']['book'][2],
|
518
529
|
@object['store']['book'][4],
|
519
530
|
@object['store']['book'][5],
|
520
531
|
@object['store']['book'][6]
|
521
532
|
], JsonPath.new('$..book[?(@.author =~ /herman|lukyanenko/i)]').on(@object)
|
522
|
-
|
533
|
+
end
|
534
|
+
|
535
|
+
def test_regex_error
|
536
|
+
assert_raises ArgumentError do
|
537
|
+
JsonPath.new('$.store.book..tags[?(@ =~ asdf)]').on(@object)
|
538
|
+
end
|
523
539
|
end
|
524
540
|
|
525
541
|
def test_regression_1
|
@@ -748,7 +764,7 @@ class TestJsonpath < MiniTest::Unit::TestCase
|
|
748
764
|
end
|
749
765
|
|
750
766
|
def test_runtime_error_frozen_string
|
751
|
-
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')
|
767
|
+
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')
|
752
768
|
json = '
|
753
769
|
{
|
754
770
|
"test": "something"
|
@@ -804,6 +820,131 @@ class TestJsonpath < MiniTest::Unit::TestCase
|
|
804
820
|
assert_equal expected, JsonPath.for(a.to_json).delete('$.itemList[1:6:2]').to_hash
|
805
821
|
end
|
806
822
|
|
823
|
+
def test_nested_values
|
824
|
+
json = '
|
825
|
+
{
|
826
|
+
"phoneNumbers": [
|
827
|
+
[{
|
828
|
+
"type" : "iPhone",
|
829
|
+
"number": "0123-4567-8888"
|
830
|
+
}],
|
831
|
+
[{
|
832
|
+
"type" : "home",
|
833
|
+
"number": "0123-4567-8910"
|
834
|
+
}]
|
835
|
+
]
|
836
|
+
}
|
837
|
+
'.to_json
|
838
|
+
assert_equal [[{ 'type' => 'home', 'number' => '0123-4567-8910' }]], JsonPath.on(json, "$.phoneNumbers[?(@[0].type == 'home')]")
|
839
|
+
assert_equal [], JsonPath.on(json, "$.phoneNumbers[?(@[2].type == 'home')]")
|
840
|
+
json = '
|
841
|
+
{
|
842
|
+
"phoneNumbers":
|
843
|
+
{
|
844
|
+
"type" : "iPhone",
|
845
|
+
"number": "0123-4567-8888"
|
846
|
+
}
|
847
|
+
}
|
848
|
+
'.to_json
|
849
|
+
assert_equal [], JsonPath.on(json, "$.phoneNumbers[?(@[0].type == 'home')]")
|
850
|
+
end
|
851
|
+
|
852
|
+
def test_selecting_multiple_keys
|
853
|
+
json = '
|
854
|
+
{
|
855
|
+
"store": {
|
856
|
+
"book": [
|
857
|
+
{
|
858
|
+
"category": "reference",
|
859
|
+
"author": "Nigel Rees",
|
860
|
+
"title": "Sayings of the Century",
|
861
|
+
"price": 8.95
|
862
|
+
},
|
863
|
+
{
|
864
|
+
"category": "fiction",
|
865
|
+
"author": "Evelyn Waugh",
|
866
|
+
"title": "Sword of Honour",
|
867
|
+
"price": 12.99
|
868
|
+
}
|
869
|
+
]
|
870
|
+
}
|
871
|
+
}
|
872
|
+
'.to_json
|
873
|
+
|
874
|
+
assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }, { 'category' => 'fiction', 'author' => 'Evelyn Waugh' }], JsonPath.on(json, '$.store.book[*](category,author)')
|
875
|
+
end
|
876
|
+
|
877
|
+
def test_selecting_multiple_keys_with_filter
|
878
|
+
json = '
|
879
|
+
{
|
880
|
+
"store": {
|
881
|
+
"book": [
|
882
|
+
{
|
883
|
+
"category": "reference",
|
884
|
+
"author": "Nigel Rees",
|
885
|
+
"title": "Sayings of the Century",
|
886
|
+
"price": 8.95
|
887
|
+
},
|
888
|
+
{
|
889
|
+
"category": "fiction",
|
890
|
+
"author": "Evelyn Waugh",
|
891
|
+
"title": "Sword of Honour",
|
892
|
+
"price": 12.99
|
893
|
+
}
|
894
|
+
]
|
895
|
+
}
|
896
|
+
}
|
897
|
+
'.to_json
|
898
|
+
|
899
|
+
assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, "$.store.book[?(@['price'] == 8.95)](category,author)")
|
900
|
+
assert_equal [{ 'category' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, "$.store.book[?(@['price'] == 8.95)]( category, author )")
|
901
|
+
end
|
902
|
+
|
903
|
+
def test_selecting_multiple_keys_with_filter_with_space_in_catergory
|
904
|
+
json = '
|
905
|
+
{
|
906
|
+
"store": {
|
907
|
+
"book": [
|
908
|
+
{
|
909
|
+
"cate gory": "reference",
|
910
|
+
"author": "Nigel Rees",
|
911
|
+
"title": "Sayings of the Century",
|
912
|
+
"price": 8.95
|
913
|
+
},
|
914
|
+
{
|
915
|
+
"cate gory": "fiction",
|
916
|
+
"author": "Evelyn Waugh",
|
917
|
+
"title": "Sword of Honour",
|
918
|
+
"price": 12.99
|
919
|
+
}
|
920
|
+
]
|
921
|
+
}
|
922
|
+
}
|
923
|
+
'.to_json
|
924
|
+
|
925
|
+
assert_equal [{ 'cate gory' => 'reference', 'author' => 'Nigel Rees' }], JsonPath.on(json, "$.store.book[?(@['price'] == 8.95)]( cate gory, author )")
|
926
|
+
end
|
927
|
+
|
928
|
+
def test_object_method_send
|
929
|
+
j = {height: 5, hash: "some_hash"}.to_json
|
930
|
+
hs = JsonPath.new "$..send"
|
931
|
+
assert_equal([], hs.on(j))
|
932
|
+
hs = JsonPath.new "$..hash"
|
933
|
+
assert_equal(["some_hash"], hs.on(j))
|
934
|
+
hs = JsonPath.new "$..send"
|
935
|
+
assert_equal([], hs.on(j))
|
936
|
+
j = {height: 5, send: "should_still_work"}.to_json
|
937
|
+
hs = JsonPath.new "$..send"
|
938
|
+
assert_equal(['should_still_work'], hs.on(j))
|
939
|
+
end
|
940
|
+
|
941
|
+
def test_index_access_by_number
|
942
|
+
data = {
|
943
|
+
'1': 'foo'
|
944
|
+
}
|
945
|
+
assert_equal ['foo'], JsonPath.new('$.1').on(data.to_json)
|
946
|
+
end
|
947
|
+
|
807
948
|
def example_object
|
808
949
|
{ 'store' => {
|
809
950
|
'book' => [
|
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.0.
|
4
|
+
version: 1.0.7
|
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: 2020-12-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -25,20 +25,6 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: to_regexp
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - "~>"
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: 0.2.1
|
35
|
-
type: :runtime
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - "~>"
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: 0.2.1
|
42
28
|
- !ruby/object:Gem::Dependency
|
43
29
|
name: bundler
|
44
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,22 +129,21 @@ licenses:
|
|
143
129
|
- MIT
|
144
130
|
metadata: {}
|
145
131
|
post_install_message:
|
146
|
-
rdoc_options:
|
147
|
-
- "--charset=UTF-8"
|
132
|
+
rdoc_options: []
|
148
133
|
require_paths:
|
149
134
|
- lib
|
150
135
|
required_ruby_version: !ruby/object:Gem::Requirement
|
151
136
|
requirements:
|
152
137
|
- - ">="
|
153
138
|
- !ruby/object:Gem::Version
|
154
|
-
version: '
|
139
|
+
version: '2.5'
|
155
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
141
|
requirements:
|
157
142
|
- - ">="
|
158
143
|
- !ruby/object:Gem::Version
|
159
144
|
version: '0'
|
160
145
|
requirements: []
|
161
|
-
rubygems_version: 3.0.
|
146
|
+
rubygems_version: 3.0.3
|
162
147
|
signing_key:
|
163
148
|
specification_version: 4
|
164
149
|
summary: Ruby implementation of http://goessner.net/articles/JsonPath/
|