riffraff_jsonpath 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.markdown +102 -0
- data/Rakefile +68 -0
- data/VERSION +1 -0
- data/jsonpath.gemspec +59 -0
- data/lib/jsonpath.rb +22 -0
- data/lib/jsonpath/nodes.rb +213 -0
- data/lib/jsonpath/parser.rb +1888 -0
- data/lib/jsonpath/parser.treetop +106 -0
- data/test/parser_test.rb +199 -0
- data/test/reference_test.rb +136 -0
- data/test/test_helper.rb +37 -0
- metadata +80 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
grammar JSONPathGrammar
|
2
|
+
|
3
|
+
rule path
|
4
|
+
root selectors:child+ <JSONPath::Nodes::RootNode>
|
5
|
+
end
|
6
|
+
|
7
|
+
rule root
|
8
|
+
'$'
|
9
|
+
end
|
10
|
+
|
11
|
+
rule number
|
12
|
+
'-'? [0-9]+
|
13
|
+
end
|
14
|
+
|
15
|
+
rule wildcard
|
16
|
+
'*' / '\'' '*' '\''
|
17
|
+
end
|
18
|
+
|
19
|
+
rule lower
|
20
|
+
descendant / '.'
|
21
|
+
end
|
22
|
+
|
23
|
+
rule descendant
|
24
|
+
'..'
|
25
|
+
end
|
26
|
+
|
27
|
+
rule word_list
|
28
|
+
quoted_word ',' ' '* word_list
|
29
|
+
/
|
30
|
+
quoted_word
|
31
|
+
end
|
32
|
+
|
33
|
+
rule quoted_word
|
34
|
+
'\'' key:(!'\'' . )+ '\''
|
35
|
+
end
|
36
|
+
|
37
|
+
rule child
|
38
|
+
# .foo or ..foo
|
39
|
+
lower key:bareword <JSONPath::Nodes::KeyNode>
|
40
|
+
/
|
41
|
+
# .* or ..*
|
42
|
+
lower wildcard <JSONPath::Nodes::WildcardNode>
|
43
|
+
/
|
44
|
+
# '?(@ % 2 == 0)'
|
45
|
+
lower '\'?(' template_code:(!')\'' . )+ ')\'' <JSONPath::Nodes::FilterNode>
|
46
|
+
/
|
47
|
+
# .'foo'
|
48
|
+
lower '\'' key:(!'\'' . )+ '\'' <JSONPath::Nodes::KeyNode>
|
49
|
+
/
|
50
|
+
# [*] and ['*']
|
51
|
+
'[' wildcard ']' <JSONPath::Nodes::WildcardNode>
|
52
|
+
/
|
53
|
+
# [0]
|
54
|
+
'[' index:number ']' <JSONPath::Nodes::IndexNode>
|
55
|
+
/
|
56
|
+
# .[0] and ..[0]
|
57
|
+
lower '[' index:number ']' <JSONPath::Nodes::IndexNode>
|
58
|
+
/
|
59
|
+
# [1:2]
|
60
|
+
'[' start:number ':' stop:number ']' <JSONPath::Nodes::SliceNode>
|
61
|
+
/
|
62
|
+
# .[1:2] and ..[1:2]
|
63
|
+
lower '[' start:number ':' stop:number ']' <JSONPath::Nodes::SliceNode>
|
64
|
+
/
|
65
|
+
# [1:]
|
66
|
+
'[' start:number ':]' <JSONPath::Nodes::SliceNode>
|
67
|
+
/
|
68
|
+
# .[1:] and ..[1:]
|
69
|
+
lower '[' start:number ':]' <JSONPath::Nodes::SliceNode>
|
70
|
+
/
|
71
|
+
# [1:4:2]
|
72
|
+
'[' start:number ':' stop:number ':' step:number ']' <JSONPath::Nodes::SliceNode>
|
73
|
+
/
|
74
|
+
# .[1:4:2] and ..[1:4:2]
|
75
|
+
lower '[' start:number ':' stop:number ':' step:number ']' <JSONPath::Nodes::SliceNode>
|
76
|
+
/
|
77
|
+
# [1::2]
|
78
|
+
'[' start:number '::' step:number ']' <JSONPath::Nodes::SliceNode>
|
79
|
+
/
|
80
|
+
# .[1::2] and ..[1::2]
|
81
|
+
lower '[' start:number '::' step:number ']' <JSONPath::Nodes::SliceNode>
|
82
|
+
/
|
83
|
+
# [(@.length - 1)]
|
84
|
+
'[(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::ExprNode>
|
85
|
+
/
|
86
|
+
# .[(@.length - 1)] and ..[(@.length - 1)]
|
87
|
+
lower '[(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::ExprNode>
|
88
|
+
/
|
89
|
+
# [?(@ % 2 == 0)]
|
90
|
+
'[?(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::FilterNode>
|
91
|
+
/
|
92
|
+
# .[?(@ % 2 == 0)] and ..[?(@ % 2 == 0)]
|
93
|
+
lower '[?(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::FilterNode>
|
94
|
+
/
|
95
|
+
# ['foo']
|
96
|
+
'[' word_list ']' <JSONPath::Nodes::KeyNode>
|
97
|
+
/
|
98
|
+
# .['foo'] and ..['foo]
|
99
|
+
lower '[' word_list ']' <JSONPath::Nodes::KeyNode>
|
100
|
+
end
|
101
|
+
|
102
|
+
rule bareword
|
103
|
+
[_a-zA-Z0-9]+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/test/parser_test.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
|
3
|
+
# The content below was taken from the tests for the JS and PHP
|
4
|
+
# reference implementations at http://code.google.com/p/jsonpath/
|
5
|
+
class ParserTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context 'Traversing' do
|
8
|
+
context "hash" do
|
9
|
+
should "handle underscore in field names" do
|
10
|
+
path = '$.a_b'
|
11
|
+
assert_resolves({"a_b" => 1}, path, [1])
|
12
|
+
end
|
13
|
+
should "parse bareword child with single terminal" do
|
14
|
+
path = '$.a'
|
15
|
+
assert_resolves({"a" => 1}, path, [1])
|
16
|
+
end
|
17
|
+
should "parse subscripted quoted child with single terminal" do
|
18
|
+
path = "$['a b']"
|
19
|
+
assert_resolves({"a b" => 1}, path, [1])
|
20
|
+
end
|
21
|
+
should "parse subscripted quoted child and chained bareword" do
|
22
|
+
path = "$['a b'].c"
|
23
|
+
assert_resolves({"a b" => {"c" => 1}}, path, [1])
|
24
|
+
end
|
25
|
+
should "parses bare wildcard" do
|
26
|
+
path = "$.*"
|
27
|
+
assert_resolves({"a" => 1, "b" => 2}, path, [1, 2])
|
28
|
+
end
|
29
|
+
should "parse quoted wildcard on hash" do
|
30
|
+
path = "$['*']"
|
31
|
+
assert_resolves({"a" => 1, "b" => 2}, path, [1, 2])
|
32
|
+
end
|
33
|
+
should "parse quoted key outside of brackets" do
|
34
|
+
path = "$.'a b'"
|
35
|
+
assert_resolves({"a b" => 1}, path, [1])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
context "array" do
|
39
|
+
should "parse bare wildcard" do
|
40
|
+
path = "$.*"
|
41
|
+
assert_resolves([1, 2, 3], path, [1, 2, 3])
|
42
|
+
end
|
43
|
+
should "parses subscripted quoted wildcard" do
|
44
|
+
path = "$['*']"
|
45
|
+
assert_resolves([1, 2, 3], path, [1, 2, 3])
|
46
|
+
end
|
47
|
+
should "parses through bare wildcard" do
|
48
|
+
path = "$.*.name"
|
49
|
+
assert_resolves([{"name" => 1}, {"name" => 2}, {"name" => 3}], path, [1, 2, 3])
|
50
|
+
end
|
51
|
+
should "parse index to single terminal" do
|
52
|
+
path = "$[1]"
|
53
|
+
assert_resolves(%w(foo bar baz), path, %w(bar))
|
54
|
+
end
|
55
|
+
should "parse index to multiple terminals" do
|
56
|
+
path = "$.*[1].name"
|
57
|
+
assert_resolves({
|
58
|
+
"a" => [1, {"name" => 2}, 3],
|
59
|
+
"b" => [4, {"name" => 5}, 6],
|
60
|
+
"c" => [7, {"name" => 8}, 9],
|
61
|
+
}, path, [2, 5, 8])
|
62
|
+
end
|
63
|
+
should "find all descendants in a array selection" do
|
64
|
+
path = "$.[0]..name"
|
65
|
+
assert_resolves([[
|
66
|
+
{'name'=>'joe'},
|
67
|
+
{'name'=>'jean'},
|
68
|
+
{'name'=>'jane'}]],
|
69
|
+
path, ['joe', 'jean', 'jane'])
|
70
|
+
path = "$.ary[0]..name"
|
71
|
+
assert_resolves({
|
72
|
+
"ary"=>[[
|
73
|
+
{'name'=>'joe'},
|
74
|
+
{'name'=>'jean'},
|
75
|
+
{'name'=>'jane'}]]},
|
76
|
+
path, ['joe', 'jean', 'jane'])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
context "combination wildcards" do
|
80
|
+
should "parses through bare wildcard on array with additional wildcard" do
|
81
|
+
path = "$.*.names.*"
|
82
|
+
assert_resolves([
|
83
|
+
{"names" => %w(foo bar)},
|
84
|
+
{"names" => %w(baz quux)},
|
85
|
+
{"names" => %w(spam eggs)}
|
86
|
+
], path, %w(foo bar baz quux spam eggs))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
context "using slices" do
|
90
|
+
setup {
|
91
|
+
@deep = [
|
92
|
+
{"a" => {"name" => "a1"}, "b" => {"name" => "b1"}},
|
93
|
+
{"c" => {"name" => "c1"}, "d" => {"name" => "d1"}},
|
94
|
+
{"e" => {"name" => "e1"}, "f" => {"name" => "f1"}},
|
95
|
+
{"g" => {"name" => "g1"}, "h" => {"name" => "h1"}},
|
96
|
+
{"i" => {"name" => "i1"}, "j" => {"name" => "j1"}},
|
97
|
+
{"k" => {"name" => "k1"}, "l" => {"name" => "l1"}},
|
98
|
+
{"m" => {"name" => "m1"}, "n" => {"name" => "n1"}},
|
99
|
+
]
|
100
|
+
@shallow = [1, 2, 3, 4, 5, 6]
|
101
|
+
}
|
102
|
+
context "with explicit start and stop" do
|
103
|
+
context "with implicit step" do
|
104
|
+
should "parse to single terminal" do
|
105
|
+
path = '$[2:4]'
|
106
|
+
assert_resolves(@shallow, path, [3, 4, 5])
|
107
|
+
end
|
108
|
+
should "parse to multiple terminals" do
|
109
|
+
path = '$[2:4].*.name'
|
110
|
+
assert_resolves(@deep, path, %w(e1 f1 g1 h1 i1 j1))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
context "with explicit step" do
|
114
|
+
should "parse to single terminal" do
|
115
|
+
path = '$[2:4:2]'
|
116
|
+
assert_resolves(@shallow, path, [3, 5])
|
117
|
+
end
|
118
|
+
should "parse to multiple terminals" do
|
119
|
+
path = '$[2:4:2].*.name'
|
120
|
+
assert_resolves(@deep, path, %w(e1 f1 i1 j1))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
context "with explicit start and implict stop" do
|
125
|
+
context "with implicit step" do
|
126
|
+
should "parse to single terminal" do
|
127
|
+
path = '$[2:]'
|
128
|
+
assert_resolves(@shallow, path, [3, 4, 5, 6])
|
129
|
+
end
|
130
|
+
should "parse to multiple terminals" do
|
131
|
+
path = '$[2:].*.name'
|
132
|
+
assert_resolves(@deep, path, %w(e1 f1 g1 h1 i1 j1 k1 l1 m1 n1))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
context "with explicit step" do
|
136
|
+
should "parse to single terminal" do
|
137
|
+
path = '$[2::2]'
|
138
|
+
assert_resolves(@shallow, path, [3, 5])
|
139
|
+
end
|
140
|
+
should "parse to multiple terminals" do
|
141
|
+
path = '$[2::2].*.name'
|
142
|
+
assert_resolves(@deep, path, %w(e1 f1 i1 j1 m1 n1))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
context "supporting filters in Ruby" do
|
148
|
+
setup do
|
149
|
+
@numbers = [1, 2, 3, 4, 5, 6, 7, 8]
|
150
|
+
@hashes = [
|
151
|
+
{"name" => 'Bruce', "age" => 29},
|
152
|
+
{"name" => "Braedyn", "age" => 3},
|
153
|
+
{"name" => "Jamis", "age" => 2},
|
154
|
+
]
|
155
|
+
end
|
156
|
+
context "when using self-contained single statements" do
|
157
|
+
should "support simple object operations" do
|
158
|
+
path = '$[?(@ % 2 == 0)]'
|
159
|
+
assert_resolves(@numbers, path, [2, 4, 6, 8])
|
160
|
+
end
|
161
|
+
should "support manual object pathing" do
|
162
|
+
path = %($[?(@['age'] % 2 == 0)].name)
|
163
|
+
assert_resolves(@hashes, path, ["Jamis"])
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
context "descendants" do
|
169
|
+
setup do
|
170
|
+
@object = {
|
171
|
+
"a" => [1, 2, [3, 4]],
|
172
|
+
"b" => {
|
173
|
+
"c" => 5,
|
174
|
+
"e" => [6, 7]
|
175
|
+
}
|
176
|
+
}
|
177
|
+
end
|
178
|
+
should "be found with wildcard" do
|
179
|
+
path = '$..*'
|
180
|
+
assert_resolves(@object, path, [[1, 2, [3, 4]], 1, 2, [3, 4], 3, 4, {"c" => 5, "e" => [6, 7]}, 5, [6, 7], 6, 7, @object])
|
181
|
+
end
|
182
|
+
should "be found with deeper key" do
|
183
|
+
path = '$..e'
|
184
|
+
assert_resolves(@object, path, [[6, 7]])
|
185
|
+
end
|
186
|
+
should "be found with deeper index" do
|
187
|
+
path = '$..[0]'
|
188
|
+
assert_resolves(@object, path, [1, 3, 6])
|
189
|
+
end
|
190
|
+
should "resolve deeper chained selectors" do
|
191
|
+
path = '$..e[?(@ % 2 == 0)]'
|
192
|
+
assert_resolves(@object, path, [6])
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# The content below was taken from the tests for the JS and PHP
|
5
|
+
# reference implementations at http://code.google.com/p/jsonpath/
|
6
|
+
class ReferenceTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
context 'Sample 1' do
|
9
|
+
setup { @json = %({"a":"a","b":"b","c d":"e"}) }
|
10
|
+
should 'resolve a simple path' do
|
11
|
+
assert_resolves(object, "$.a", ["a"])
|
12
|
+
end
|
13
|
+
should 'resolve a path with quotes in brackets' do
|
14
|
+
assert_resolves(object, "$['a']", ["a"])
|
15
|
+
end
|
16
|
+
should 'resolve a path with a space' do
|
17
|
+
assert_resolves(object, "$.'c d'", ["e"])
|
18
|
+
end
|
19
|
+
should 'resolve a star' do
|
20
|
+
assert_resolves(object, "$.*", ["a", "b", "e"])
|
21
|
+
end
|
22
|
+
should 'resolve a star with quotes in brackets' do
|
23
|
+
assert_resolves(object, "$['*']", ["a", "b", "e"])
|
24
|
+
end
|
25
|
+
should 'resolve a star with quotes' do
|
26
|
+
assert_resolves(object, "$[*]", ["a", "b", "e"])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
context 'Sample 2' do
|
30
|
+
setup { @json = %([1, "2", 3.14, true, null]) }
|
31
|
+
should 'resolve with a number in brackets' do
|
32
|
+
assert_resolves(object, "$[0]", [1])
|
33
|
+
end
|
34
|
+
should 'resolve another number in brackets' do
|
35
|
+
assert_resolves(object, "$[4]", [nil])
|
36
|
+
end
|
37
|
+
should 'resolve a star in brackets' do
|
38
|
+
assert_resolves(object, "$[*]", [1, "2", 3.14, true, nil])
|
39
|
+
end
|
40
|
+
should 'resolve an end slice' do
|
41
|
+
assert_resolves(object, "$[-1:]", [nil])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
context 'Sample 3' do
|
45
|
+
setup { @json = %({"points":[{"id": "i1", "x": 4, "y": -5}, {"id": "i2", "x": -2, "y": 2, "z": 1}, {"id": "i3", "x": 8, "y": 3}, {"id": "i4", "x": -6, "y": -1}, {"id": "i5", "x": 0, "y": 2, "z": 1}, {"id": "i6", "x": 1, "y": 4}]}) }
|
46
|
+
should 'resolve correctly' do
|
47
|
+
assert_resolves(object, "$.points[1]", [{"id" => "i2", "x" => -2, "y" => 2, "z" => 1}])
|
48
|
+
end
|
49
|
+
should 'resolve a chained path' do
|
50
|
+
assert_resolves(object, "$.points[4].x", [0])
|
51
|
+
end
|
52
|
+
should 'resolve by attribute match' do
|
53
|
+
assert_resolves(object, "$.points[?(@['id']=='i4')].x", [-6])
|
54
|
+
end
|
55
|
+
should 'resolve a chained path with a star in brackets' do
|
56
|
+
assert_resolves(object, "$.points[*].x", [4, -2, 8, -6, 0, 1])
|
57
|
+
end
|
58
|
+
should 'resolve by attribute operation' do
|
59
|
+
assert_resolves(object, "$['points'][?(@['x']*@['x']+@['y']*@['y'] > 50)].id", ["i3"])
|
60
|
+
end
|
61
|
+
should 'resolve by attribute existence' do
|
62
|
+
assert_resolves(object, "$.points[?(@['z'])].id", ["i2", "i5"])
|
63
|
+
end
|
64
|
+
should 'resolve by length property operation' do
|
65
|
+
assert_resolves(object, "$.points[(@.length-1)].id", ["i6"])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
context 'Sample 4' do
|
69
|
+
setup { @json = %({"menu":{"header":"SVG Viewer","items":[{"id": "Open"}, {"id": "OpenNew", "label": "Open New"}, null, {"id": "ZoomIn", "label": "Zoom In"}, {"id": "ZoomOut", "label": "Zoom Out"}, {"id": "OriginalView", "label": "Original View"}, null, {"id": "Quality"}, {"id": "Pause"}, {"id": "Mute"}, null, {"id": "Find", "label": "Find..."}, {"id": "FindAgain", "label": "Find Again"}, {"id": "Copy"}, {"id": "CopyAgain", "label": "Copy Again"}, {"id": "CopySVG", "label": "Copy SVG"}, {"id": "ViewSVG", "label": "View SVG"}, {"id": "ViewSource", "label": "View Source"}, {"id": "SaveAs", "label": "Save As"}, null, {"id": "Help"}, {"id": "About", "label": "About Adobe CVG Viewer..."}]}}) }
|
70
|
+
should 'resolve testing on attribute' do
|
71
|
+
assert_resolves(object, "$.menu.items[?(@ && @['id'] && !@['label'])].id", ["Open", "Quality", "Pause", "Mute", "Copy", "Help"])
|
72
|
+
end
|
73
|
+
should 'resolve testing on attribute with regular expression' do
|
74
|
+
assert_resolves(object, "$.menu.items[?(@ && @['label'] && @['label'] =~ /SVG/)].id", ["CopySVG", "ViewSVG"])
|
75
|
+
end
|
76
|
+
should 'resolve on negative' do
|
77
|
+
# !nil == true in Ruby
|
78
|
+
assert_resolves(object, "$.menu.items[?(!@)]", [nil, nil, nil, nil])
|
79
|
+
end
|
80
|
+
should 'resolve descendant with number in brackets' do
|
81
|
+
assert_resolves(object, "$..[0]", [{"id" => "Open"}])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
context 'Sample 5' do
|
85
|
+
setup { @json = %({"a":[1, 2, 3, 4],"b":[5, 6, 7, 8]}) }
|
86
|
+
should 'resolve descendant with number in brackets' do
|
87
|
+
assert_resolves(object, "$..[0]", [1, 5])
|
88
|
+
end
|
89
|
+
should 'resolve descendant last items' do
|
90
|
+
assert_resolves(object, "$..[-1:]", [4, 8])
|
91
|
+
end
|
92
|
+
should 'resolve by descendant value' do
|
93
|
+
assert_resolves(object, "$..[?(@.is_a?(Numeric) && @ % 2 == 0)]", [2, 4, 6, 8])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
context 'Sample 6' do
|
97
|
+
setup { @json = %({"lin":{"color":"red","x":2,"y":3},"cir":{"color":"blue","x":5,"y":2,"r":1},"arc":{"color":"green","x":2,"y":4,"r":2,"phi0":30,"dphi":120},"pnt":{"x":0,"y":7}}) }
|
98
|
+
should 'resolve by operation in quotes' do
|
99
|
+
assert_resolves(object, "$.'?(@['color'])'.x", [2, 5, 2])
|
100
|
+
end
|
101
|
+
should 'resolve by multiple quoted values in brackets' do
|
102
|
+
assert_resolves(object, "$['lin','cir'].color", ["red", "blue"])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
context 'Sample 7' do
|
106
|
+
setup { @json = %({"text":["hello", "world2.0"]}) }
|
107
|
+
should 'resolve correctly filter 1' do
|
108
|
+
assert_resolves(object, "$.text[?(@.length > 5)]", ["world2.0"])
|
109
|
+
end
|
110
|
+
should 'resolve correctly filter 2' do
|
111
|
+
assert_resolves(object, "$.text[?(@[0, 1] == 'h')]", ["hello"])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
context 'Sample 8' do
|
115
|
+
setup { @json = %({"a":{"a":2,"b":3},"b":{"a":4,"b":5},"c":{"a":{"a":6,"b":7},"c":8}}) }
|
116
|
+
should 'resolve descendant' do
|
117
|
+
assert_resolves(object, "$..a", [{"a" => 2, "b" => 3}, 2, 4, {"a" => 6, "b" => 7}, 6])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
context 'Sample 10' do
|
121
|
+
setup { @json = %({"a":[{"a": 5, "@": 2, "$": 3}, {"a": 6, "@": 3, "$": 4}, {"a": 7, "@": 4, "$": 5}]}) }
|
122
|
+
should 'resolve with quoted operation and escaped special character' do
|
123
|
+
assert_resolves(object, "$.a[?(@['\\@']==3)]", [{"a" => 6, "@" => 3, "$" => 4}])
|
124
|
+
end
|
125
|
+
should 'resolve with quotes and brackets in operation' do
|
126
|
+
assert_resolves(object, "$.a[?(@['$']==5)]", [{"a" => 7, "@" => 4, "$" => 5}])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def object
|
133
|
+
JSON.parse(@json)
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
require 'jsonpath'
|
8
|
+
|
9
|
+
class Test::Unit::TestCase
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def parser
|
14
|
+
@parser ||= JSONPath::Parser.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse(path)
|
18
|
+
parser.parse(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def assert_parses(path)
|
22
|
+
result = parse(path)
|
23
|
+
assert result, parser.failure_reason
|
24
|
+
end
|
25
|
+
|
26
|
+
def assert_resolves(obj, path, result)
|
27
|
+
assert_parses path
|
28
|
+
assert_equal safe_sort(result), safe_sort(parse(path).walk(obj))
|
29
|
+
end
|
30
|
+
|
31
|
+
def safe_sort(objs)
|
32
|
+
objs.sort_by do |obj|
|
33
|
+
obj ? obj.to_s : 0.to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|